Windows上使用Python和subprocess.Popen()的Unicode文件名
为什么会发生以下情况:
>>> u'\u0308'.encode('mbcs') #UMLAUT '\xa8' >>> u'\u041A'.encode('mbcs') #CYRILLIC CAPITAL LETTER KA '?' >>>
我有一个Python应用程序接受来自操作系统的文件名。 它适用于一些国际用户,但不适用于其他用户。
例如,这个unicode文件名:u'\ u041a \ u0433 \ u044b \ u04448 \ u0444 \ u0442'
不会使用Windows'mbcs'编码(文件系统使用的编码,由sys.getfilesystemencoding()返回)进行编码。 我得到'???????',表示编码器在这些字符上失败。 但是这是没有意义的,因为文件名是从用户开始的。
更新:这是我背后的原因背景…我在我的系统上有一个文件名称在西里尔文。 我想调用subprocess.Popen()与该文件作为参数。 Popen不会处理unicode。 通常情况下,我可以用sys.getfilesystemencoding()给出的编解码器对参数进行编码。 在这种情况下,它不会工作
在Py3K中 – 至less从Python 3.2中 – subprocess.Popen
和sys.argv
与Windows上的(默认unicode)string一致。 CreateProcessW
和GetCommandLineW
被明显使用。
在Python中 – 至多v2.7.2至less – subprocess.Popen
与Unicode参数错误。 它坚持CreateProcessA
(而os.*
与Unicode一致)。 shlex.split
创build了额外的废话。
Pywin32的win32process.CreateProcess
也不会自动切换到W版本,也没有win32process.CreateProcessW
。 与GetCommandLine
。 因此需要使用ctypes.windll.kernel32.CreateProcessW...
对于这个问题,subprocess模块也许应该是固定的。
使用私有应用程序的argv[1:]
上的UTF8在Unicode操作系统上仍然笨拙。 这样的技巧对于像Linux这样的8位“Latin1”string操作系统可能是合法的。
UPDATE vaab已经创build了Popen for Python 2.7的补丁版本,可以解决这个问题。
见https://gist.github.com/vaab/2ad7051fc193167f15f85ef573e54eb9
博客文章解释: http : //vaab.blog.kal.fr/2017/03/16/fixing-windows-python-2-7-unicode-issue-with-subprocesss-popen/
sys.getfilesystemencoding()的文档说,对于Windows NT及更高版本,文件名是本地Unicode。 如果你有一个有效的unicode文件名,为什么你会打扰编码使用mbcs?
对于编解码器模块文档说,mbcs使用“ANSI代码页”(这将取决于用户的区域设置)编码,所以如果语言环境不使用西里尔字符,图示。
编辑:所以你的过程是调用subprocess.Popen()。 如果你所调用的进程在你的控制之下,这两个进程可以同意使用UTF-8作为Unicode传输格式。 否则,您可能需要在pywin32邮件列表上询问。 在任何情况下,编辑您的问题来说明您对调用过程的控制程度。
如果您需要传递现有文件的名称,则可以通过传递Unicode版本的8.3版本来获得更好的成功机会。
你需要安装pywin32软件包,然后你可以这样做:
>>> import win32api >>> win32api.GetShortPathName(u"C:\\Program Files") 'C:\\PROGRA~1'
我相信这些短文件名只使用ASCII字符,因此您应该可以将它们用作命令行的参数。
如果还需要指定要创build的文件名,可以使用Unicode文件名从Python预先创build大小为零的文件,并将文件的短名称作为parameter passing。
更新:用户bogdan正确地说8.3文件名生成可以被禁用(当我在我的笔记本电脑上安装Windows XP时,我也禁用了它),所以你不能依赖它们。 因此,作为NTFS卷上的另一个更为牵强的方法,可以将Unicode文件名硬链接到纯ASCII文件。 将ASCII文件名传递给外部命令,然后删除它们。
免责声明:我是下面提到的修复的作者。
为了在python 2.7上支持windows上的unicode命令行,你可以使用这个补丁来进行subprocess.Popen(..)
情况
python 2对windows的unicode命令行的支持很差。
严重窃听:
-
从调用方(通过
subprocess.Popen(..)
)向系统发出unicode命令行, -
并从被调用方(通过
sys.argv
)读取当前的命令行unicode参数,
这是公认的, 将不会固定在Python 2上。这些在Python 3中得到了修复。
技术原因
在Python 2中, subprocess.Popen(..)
和sys.argv
windows实现使用非unicode就绪的windows系统调用CreateProcess(..)
(请参阅Python 代码 ,以及CreateProcess的 MSDN 文档 ),并且不使用GetCommandLineW(..)
为sys.argv
。
在Python 3中, subprocess.Popen(..)
windows实现使用从3.0
(参见3.0
代码 CreateProcessW(..)
开始的正确的Windows系统调用CreateProcessW(..)
,并且sys.argv
使用从3.3
开始的GetCommandLineW(..)
见3.3
代码 )。
它是如何修复的
给定的补丁将利用ctypes
模块直接调用C的Windows系统CreateProcessW(..)
。 它通过覆盖私有方法Popen._execute_child(..)
和私有函数_subprocess.CreateProcess(..)
来build立和使用Windows系统库中的CreateProcessW(..)
,以尽可能模仿的方式提出一个新的固定Popen
对象它是如何在Python 3.6
完成的。
如何使用它
博客文章解释说明如何使用给定的补丁。 它还显示了如何用另一个修复程序读取当前进程sys.argv
。