我有边缘案例问题。我的Python script_A.py有这段代码(缩写)。
script_A.py:
from __future__ import unicode_literals
import subprocess
executable = 'sample.exe'
kwargs['bufsize'] = 0
kwargs['executable'] = executable
kwargs['stdin'] = subprocess.PIPE
kwargs['stdout'] = subprocess.PIPE
kwargs['stderr'] = subprocess.PIPE
kwargs['preexec_fn'] = None
kwargs['close_fds'] = False
kwargs['shell'] = False
kwargs['cwd'] = None
kwargs['env'] = None
kwargs['universal_newlines'] = True
kwargs['startupinfo'] = None
kwargs['creationflags'] = 0
if sys.version_info.major == 3 and sys.version_info.minor > 5:
kwargs['encoding'] = 'utf-8'
args = ['', '-x']
subproc = subprocess.Popen(args, **kwargs)
# service subproc.stdout and subproc.stderr on threads
stdout = _start_thread(_get_stdout, subproc)
stderr = _start_thread(_get_stderr, subproc)
with codecs.open('myutf-8.txt', encoding='utf-8') as fh:
for line in fh:
if os.name == 'nt':
subproc.stdin.write(b'%s\n' % line.rstrip().encode('utf-8'))
else:
subproc.stdin.write('%s\n' % line.rstrip()) # OFFENDING LINE BELOW
stdout.join()
此代码始终适用于Windows 8/10和Ubuntu 16.04 / 17.10上的Python 2.7.14和3.6.4。请注意,某些kwargs值在Windows上是不同的,但它们在这里无关紧要。它适用于16.04上的Python 3.5.2,但仅限于我从Gnome终端执行script_A.py时。
有时,我需要使用script_B.py来启动script_A.py而不是终端。 Script_B.py具有相同的subprocess.Popen()代码,用于启动相应的Python可执行文件。
script_B.py
if os.name == 'nt':
if use_py2:
executable = 'C:\\Python27\\python.exe'
else:
executable = 'C:\\Program Files\\Python36\\python.exe'
else:
if use_py2:
executable = '/usr/bin/python'
else:
executable = '/usr/bin/python3'
args = ['', 'script_A.py']
# ---- ditto above code from here ----
我在Python 3.5.2上使用Popen()从script_B.py执行script_A.py时出现此错误。 OS / Python版本的其他组合都没有失败。
Traceback:
File "script_A.py", line 30, in run
subproc.stdin.write('%s\n' % line.rstrip())
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)
你可以在2.7.14和3.6.4上看到,我使用特定代码强制管道为utf-8。我不知道如何在3.5.2上设置utf-8编码。
那么,有没有办法在3.5.2 Popen管道上配置编码?将Python 3.5排除在支持之外可能更容易,但我想我会问这里。
答案 0 :(得分:0)
您的输入文件是UTF-8,并且您要提供数据的程序需要UTF-8输入。所以直接发送原始二进制文件,而不是从字节到文本解码,然后从文本重新编码为字节。
摆脱启用universal_newlines
模式的行以及设置kwargs['encoding']
的行,并替换为with
提供的整个stdin
块:
blinesep = os.linesep.encode('utf-8') # Since you seem to need OS specific line endings
with open('myutf-8.txt', 'rb') as fh:
for line in fh:
subproc.stdin.writelines((sline, blinesep))
如果您愿意,您仍然可以将stdout
/ stderr
流作为文本流处理,您只需使用io.TextIOWrapper
和适当的编码明确地将它们包装起来。例如,您可以使用:
stdout
textout = io.TextIOWrapper(subproc.stdout, encoding='utf-8')
一些附注:
bufsize
时明确设置Popen
是正确的,因为如果不这样做,它就不可能在Python版本中保持一致;在Python 2和Python 3.3.0及更早版本上,默认缓冲行为(bufsize=0
)和-1
(意思是"使用合适的默认缓冲区大小")在3.3.1和后来。为了提高性能,明确使用bufsize=-1
是一个好主意;无论如何你都要对读取进行线程处理,因此缓冲死锁并不是一个问题。codecs.open
。它的错误(没有翻译行结尾,混合readline
与read(n)
调用奇怪的事情,当没有编码通过时,它没有&#39 ; t甚至包括普通open
的结果,因此API改变等等),缓慢和准弃用。如果您需要在Python 2.6及更高版本上保持一致的行为,请使用io.open
,它在Python 2.6及更高版本上一致地提供Python 3内置open
函数。