为什么会发生以下情况:
>>> u'\u0308'.encode('mbcs') #UMLAUT
'\xa8'
>>> u'\u041A'.encode('mbcs') #CYRILLIC CAPITAL LETTER KA
'?'
>>>
我有一个Python应用程序接受来自操作系统的文件名。它适用于一些国际用户,但不适用于其他用户。
例如,这个unicode文件名: 的u '\ u041a \ u0433 \ u044b \ u044b \ u0448 \ u0444 \ u0442'
不会使用Windows的'mbcs'编码进行编码(文件系统使用的编码,由sys.getfilesystemencoding()返回)。我得到'???????',表示编码器在这些字符上失败。但这没有任何意义,因为文件名来自用户开始。
更新:这是我背后原因的背景...... 我的系统上有一个文件,名字叫西里尔文。我想用该文件作为参数调用subprocess.Popen()。 Popen不会处理unicode。通常情况下,我可以使用sys.getfilesystemencoding()给出的编解码器对参数进行编码。在这种情况下,它将无法正常工作
答案 0 :(得分:8)
在Py3K中 - 至少从Python 3.2开始 - subprocess.Popen
和sys.argv
与Windows上的(默认unicode)字符串一致。显然使用CreateProcessW
和GetCommandLineW
。
在Python中 - 至少到v2.7.2 - subprocess.Popen
对于Unicode参数是错误的。它坚持CreateProcessA
(而os.*
与Unicode一致)。 shlex.split
会产生额外的废话。
Pywin32的win32process.CreateProcess
也不会自动切换到W版本,也没有win32process.CreateProcessW
。与GetCommandLine
相同。
因此需要使用ctypes.windll.kernel32.CreateProcessW...
。
关于这个问题,可能应该修复子进程模块。
argv[1:]
上的私有应用程序上的UTF8在Unicode操作系统上仍然很笨拙。对于像Linux这样的8位“Latin1”字符串操作系统来说,这样的技巧可能是合法的。
更新 vaab已为Python 2.7创建了Popen
的修补版本,修复了此问题。
见https://gist.github.com/vaab/2ad7051fc193167f15f85ef573e54eb9
博客文章解释:http://vaab.blog.kal.fr/2017/03/16/fixing-windows-python-2-7-unicode-issue-with-subprocesss-popen/
答案 1 :(得分:5)
免责声明:我是以下提到的解决方案的作者。
要使用python 2.7在Windows上支持unicode命令行,您可以使用
this patch至subprocess.Popen(..)
情况
Python 2对Windows上的unicode命令行的支持非常差。
严重错误:
从来电方向系统发出unicode命令行(通过subprocess.Popen(..)
),
并从被叫方那边读取当前命令行unicode参数(通过sys.argv
),
Python 2上已经确认并且won't be fixed。这些在Python 3中得到修复。
技术原因
在Python 2中,subprocess.Popen(..)
和sys.argv
的Windows实现使用非unicode就绪的Windows系统调用CreateProcess(..)
(请参阅python code和MSDN doc of CreateProcess )GetCommandLineW(..)
不使用sys.argv
。
在Python 3中,subprocess.Popen(..)
的Windows实现使用从CreateProcessW(..)
开始的正确的Windows系统调用3.0
(请参阅3.0
中的code)和sys.argv
从GetCommandLineW(..)
开始使用3.3
(请参阅3.3
中的code)。
如何修复
给定patch将利用ctypes
模块调用C窗口
系统CreateProcessW(..)
直接。它通过重写私有方法Popen
和私有函数Popen._execute_child(..)
来提出一个新的固定_subprocess.CreateProcess(..)
对象,以尽可能模仿的方式从windows系统lib中设置和使用CreateProcessW(..)
。如何在Python 3.6
中完成。
如何使用
这个blog post explanation演示了如何使用给定的补丁。它还显示了如何读取当前进程
sys.argv
与another fix。
答案 2 :(得分:3)
Docs for sys.getfilesystemencoding()表示对于Windows NT及更高版本,文件名本身就是Unicode。如果您有一个有效的unicode文件名,为什么还要使用mbcs编码呢?
Docs for codecs module说mbcs使用“ANSI代码页”进行编码(根据用户的语言环境而有所不同),所以如果语言环境不使用西里尔字符,则为splat。
编辑:所以你的进程正在调用subprocess.Popen()。如果您的调用进程在您的控制之下,则这两个进程应该能够同意使用UTF-8作为Unicode传输格式。否则,您可能需要在pywin32邮件列表上询问。在任何情况下,编辑您的问题以说明您对调用过程的控制程度。
答案 3 :(得分:2)
如果您需要传递现有文件的名称,那么通过传递Unicode文件名的8.3版本可能会有更大的成功机会。
您需要安装pywin32软件包,然后才能执行以下操作:
>>> import win32api
>>> win32api.GetShortPathName(u"C:\\Program Files")
'C:\\PROGRA~1'
我相信这些短文件名只使用ASCII字符,因此您应该可以将它们用作命令行的参数。
如果您还需要指定要创建的文件名,您可以使用Unicode文件名从Python中预先创建零大小,并将文件的短名称作为参数传递。
更新:用户bogdan正确地说可以禁用8.3文件名生成(当我在笔记本电脑上安装Windows XP时,我也禁用了它),所以你不能依赖它们。因此,作为处理NTFS卷时的另一种更为牵强的方法,可以将hard link的Unicode文件名{{3}}转换为纯ASCII文件名;将ASCII文件名传递给外部命令,然后删除它们。
答案 4 :(得分:0)
使用Python 3,只是不对字符串进行编码。 Windows文件名本机为Unicode,Python 3中的所有字符串均为Unicode,Popen使用CreateProcess
Windows API函数的Unicode版本。
对于Python 2.7,最简单的解决方案是使用第三方模块https://pypi.org/project/subprocessww/。没有获得完整Unicode支持(独立于系统区域设置)的“内置”解决方案,Python 2.7的维护者认为这是功能请求,而不是错误修复,因此这不会改变。
有关事物为何如此的详细技术说明,请参阅其他答案。