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中 – 至少从Python 3.2中 – subprocess.Popensys.argv与Windows上的(默认unicode)字符串一致。 CreateProcessWGetCommandLineW被明显使用。

在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”字符串操作系统来说可能是合法的。

UPDATE vaab已经创建了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字符,因此您应该可以将它们用作命令行的参数。

如果还需要指定要创建的文件名,可以使用Unicode文件名从Python预先创建大小为零的文件,并将文件的短名称作为参数传递。

更新:用户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(..)来建立和使用Windows系统库中的CreateProcessW(..) ,以尽可能模仿的方式提出一个新的固定的Popen对象它是如何在Python 3.6完成的。

如何使用它

博客文章的解释说明了如何使用给定的补丁。 它还显示了如何用另一个修复程序读取当前进程sys.argv