Python子进程输出Unicode文字面量

7
我知道之前已经有类似的问题被提出过,但我并没有找到解决方案。
我想在使用subprocess模块时,在我的python文件中定义一个unicode字面量,但我得不到需要的结果。例如下面的代码:
# -*- coding: utf-8 -*-
import sys
import codecs
import subprocess
cmd = ['echo', u'你好']
new_cmd = []
for c in cmd:
    if isinstance(c,unicode):
        c = c.encode('utf-8')
    new_cmd.append(c)
subprocess.call(new_cmd)

打印输出
你好

如果我改变代码为:
# -*- coding: utf-8 -*-
import sys
import codecs
import subprocess
cmd = ['echo', u'你好']
new_cmd = []
for c in cmd:
    if isinstance(c,unicode):
        c = c.encode(sys.getfilesystemencoding())
    new_cmd.append(c)
subprocess.call(new_cmd)

我得到下面的结果。
??

现阶段我只能假设我一次又一次地犯了一个简单的错误。但是我很难弄清楚它是什么。当通过Python的子进程调用时,如何才能让echo打印出以下内容:

你好

编辑:

Python的版本是2.7。我在Windows 8上运行,但我希望解决方案是平台无关的。


检查您的本地系统。尝试设置locale https://docs.python.org/2/library/locale.html - oxana
哦,我以为你也会遇到那个问题。@no_test提出的方向可能是一个更好的想法。 - cnluzon
@ShaneGannon,感兴趣的话,如果您传递一个字符串并使用shell=True,您会看到什么? - Padraic Cunningham
1
很奇怪,那个代码页无法显示你想要的字符。当你输入echo时它是如何工作的? - Mark Ransom
评论不是用于长时间讨论的;这次对话已经被移动到聊天室了(http://chat.stackoverflow.com/rooms/77006/discussion-on-question-by-shane-gannon-python-subprocess-echo-a-unicode-literal)。 - Taryn
显示剩余8条评论
2个回答

2

你的第一次尝试是最好的。

实际上,你将2个Unicode字符“你好”(或“\u4f60\u597d”)转换成UTF8,得到了b'\xe4\xbd\xa0\xe5\xa5\xbd'

你可以在完全支持Unicode的IDLE中控制它,其中b'\xe4\xbd\xa0\xe5\xa5\xbd'.decode('utf-8')会返回你好。另一种控制方法是将脚本输出重定向到文件,并使用UTF-8兼容编辑器打开:在那里,你也将看到想要的结果。

但问题在于Windows控制台不支持完整的Unicode。这取决于:

  • 安装的代码页 - 我不知道Windows 8是什么情况,但以前的版本对Unicode的支持很差,只能显示256个字符
  • 控制台中使用的字体 - 并非所有字体都具有所有字符的字形。

如果你知道一个包含你字符字形的代码页(我不知道),你可以尝试用chcp将其插入控制台,并将你的Unicode字符串显式编码为该代码页。但在我的法语机器上,我不知道如何做...除非通过一个文本文件!

由于你提到了ConEmu,我试了一下...它在Python 3.4下正常运行!

chcp 65001
py -3
import subprocess
cmd = ['cmd', '/c', 'echo', u'\u4f60\u597d']
subprocess.call(cmd)

提供:

你好  
0

问题只出现在 cmd.exe 窗口中!

不确定这是否真的是一个答案,因为我没有说“这样做就可以了”,但它提供了一些提示。 - Serge Ballesta
作为一个实验,我在Python解释器中运行了"print b'\xe4\xbd\xa0\xe5\xa5\xbd'.decode('utf-8')",但是我得到的只是"õ¢áÕÑ¢"。令人烦恼的是,出乎我的意料,cmd不支持"你好"。但是我仍然可以从GUI和Conemu创建一个名为"你好"的文件夹。因此,某个API/方法存在。 - Shane Gannon
@ShaneGannon:我没有使用ConEmu(看起来不错......),但是由于它内部是GUI应用程序,它可以像IDLE一样具有完全的Unicode支持。问题仅限于在cmd.exe窗口中运行的控制台应用程序。 - Serge Ballesta
真的。到目前为止,看起来cmd不支持Unicode。即使将字体切换为“Lucida Console”,我只能渲染一些Unicode字符。例如š但不是你好。 - Shane Gannon
@ShaneGannon:在ConEmu上它可以正常工作。请看我的编辑。我可以确认ConEmu使用Consolas字体,就像我的cmd.exe窗口一样... - Serge Ballesta
显示剩余2条评论

1

结论: 注意字符编码(这里有三种不同的字符编码)。如果您想要可移植的Unicode支持,请使用Python 3(将参数作为Unicode传递,不要对其进行编码),或者确保数据可以使用当前环境中的字符编码表示(在Python 2中使用sys.getfilesystemencoding()进行编码,就像在第二个代码示例中一样)。


第一个代码示例是不正确的。效果与(在IDLE中运行 -- py -3 -midlelib)相同:

>>> print(u'你好'.encode('utf-8').decode('mbcs')) #XXX DON'T DO IT!
你好

mbcs编解码器使用您的Windows ANSI code page(通常为:cp1252字符编码--在俄罗斯 Windows 上可能会有所不同,例如cp1251)。

Python 2 使用 CreateProcess 宏来启动一个子进程,相当于在那里使用 CreateProcessA 函数。 CreateProcessA 将输入字节解释为使用您的 Windows ANSI 编码进行编码的字节。它与 Python 源代码编码(在您的情况下为 utf-8)无关。

如果使用错误的编码方式,则会出现乱码。


如果输入字符可以使用Windows代码页(如cp1252)表示(以将Unicode编码为字节),并且echo使用Unicode API在Windows控制台上打印,例如WriteConsoleW()(请参见Python 3软件包win-unicode-console - 它可以使print(u'你好') 无论您的chcp(“OEM”)是什么,只要控制台中的字体支持这些字符)或者字符可以使用OEM代码页(由cmd.exe使用)表示,例如cp437(运行chcp查找您的代码页)。 ??问号表示你好不能使用您的控制台编码表示。

为了支持任意Unicode参数(包括不能使用Windows(“ANSI”)或MS-DOS(OEM)代码页表示的字符),您需要CreateProcessW函数(Python 3使用)。请参见Unicode filenames on Windows with Python & subprocess.Popen()

不幸的是,这是正确的答案。即使启用了Lucida Console字体,在Windows 8上使用Python 2.7也无法表示所有字符。我有幸转移到另一个平台以使其正常工作。 - Shane Gannon
@ShaneGannon:可以显示所有字符(至少是Lucida Console字体支持的字符),例如,您可以使用ctypes模块自己调用CreateProcessW。参见我的答案中的最后一个链接。或者您可以使用WriteConsoleW()直接从Python将Unicode写入Windows控制台。如果不需要支持Windows控制台,那么只需使用'utf-8'编码并将输出重定向到文件(或另一个程序,如果它允许指定其输入编码)。 - jfs
我不认为Lucida Console字体支持"你好"这个字符。因为我可以选择不使用Windows,所以utf-8对我来说很有效。 - Shane Gannon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接