我可以设置Python 3.5 subprocess.Popen管道编码吗?

我可以设置Python 3.5 subprocess.Popen管道编码吗?,python,python-3.x,subprocess,Python,Python 3.x,Subprocess,我有一个边缘案例问题。我的Python脚本_A.py有以下代码(缩写) 这段代码始终在Windows8/10和Ubuntu 16.04/17.10上的Python 2.7.14和3.6.4上运行。注意,窗口上的一些kwargs值不同,但在这里它们是无关的。它在16.04版本的Python3.5.2上工作,但仅当我从Gnome终端执行script_A.py时 有时,我需要使用script_B.py来启动script_A.py而不是终端。脚本_B.py具有相同的subprocess.Popen()代

我有一个边缘案例问题。我的Python脚本_A.py有以下代码(缩写)

这段代码始终在Windows8/10和Ubuntu 16.04/17.10上的Python 2.7.14和3.6.4上运行。注意,窗口上的一些kwargs值不同,但在这里它们是无关的。它在16.04版本的Python3.5.2上工作,但仅当我从Gnome终端执行script_A.py时

有时,我需要使用script_B.py来启动script_A.py而不是终端。脚本_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 ----
在Python3.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从支持中排除可能会更容易,但我想在这里问一下。

您的输入文件是UTF-8,而您正在向其提供数据的程序需要UTF-8输入。所以直接发送原始二进制文件,而不是从字节到文本进行解码,然后从文本到字节重新编码

去掉打开
universal_newlines
模式的行和设置
kwargs['encoding']
的行,并将整个
替换为馈送
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))
textout = io.TextIOWrapper(subproc.stdout, encoding='utf-8')
如果愿意,您仍然可以将
stdout
/
stderr
流作为文本流处理,只需使用
io.TextIOWrapper
和适当的编码显式包装它们。例如,您可以使用以下内容包装二进制
stdout

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))
textout = io.TextIOWrapper(subproc.stdout, encoding='utf-8')
以下是一些旁注:

  • 在调用
    Popen
    时,显式设置
    bufsize
    是正确的,因为不这样做就不可能在Python版本之间保持一致的行为;在Python 2和Python 3.3.0及更早版本上,默认缓冲行为是无缓冲的(
    bufsize=0
    ),在3.3.1及更高版本中,默认缓冲行为是无缓冲的(
    -1
    (意思是“使用适当的默认缓冲区大小”)。对于性能,显式使用
    bufsize=-1
    是一个好主意;无论如何,您都在线程化读取,因此缓冲死锁不是一个问题
  • 切勿使用
    编解码器。打开
    。这是一个错误(不翻译行结尾,将
    readline
    read(n)
    调用混合在一起会产生奇怪的结果,当没有经过编码时,它甚至不会包装plain
    open
    的结果,因此API会发生变化,等等),速度慢,并且几乎不推荐使用。如果您需要在Python 2.6及更高版本上保持一致的行为,请使用
    io.open
    ,它在Python 2.6及更高版本上提供了Python 3内置的
    open
    函数

  • 暗影游侠,非常感谢你的详细建议。我将分组发表评论。(1) ,通用新行是不相关的;我不说了。推送到.stdin()的换行符是简单的“\n”(用上面的.rstrip()修复)。(2) 感谢您提供关于bufsize=-1的提示。我会试试。(3)我知道Unidode LINE_sep和codec.open()中的其他怪癖,我单独管理它们。这个特殊的临时文件是在规范化了这些怪癖之后创建的。尽管如此,我仍然需要在编解码器的所有OS和Python系统上使用相同的行为。我将查看io.open()是否可以获得相同的一致性。我对您的encode()修复程序表示怀疑,但我还是尝试了。除了它不起作用之外(如下),我还需要使用Unicode(Py3 str)格式的行。此简化版本删除了许多规范化/过滤Unicode字符串的函数(因此需要Unicode_文字)。请注意,有问题的行
    suboc.stdin.write(“%s\n”%sline.rstrip())
    将Unicode str发送到子进程。错误消息报告Popen()在内部应用了
    'ascii'编解码器
    ,并使用非ascii字符失败。读取原始UTF-8行并将其发送到stdin.write()会引发完全相同的错误。这恰好是阿拉伯文文本,但很可能是任何有效的UTF-8 Unicode。在Python 3.6上,
    kwargs['encoding']
    配置是绝对关键的,因为它将Popen()配置为使用“ascii”编解码器替换为“UTF-8”编解码器。如果可能的话,我需要在3.5上使用相同的功能,但是在3.6中添加了“encoding”关键字。