带有Python和Linux的Windows上的Unicode文件名subprocess.Popen()

时间:2009-12-15 20:45:14

标签: python windows unicode

为什么会发生以下情况:

>>> 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()给出的编解码器对参数进行编码。在这种情况下,它将无法正常工作

5 个答案:

答案 0 :(得分:8)

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

更新 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 patchsubprocess.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 CreateProcessGetCommandLineW(..)不使用sys.argv

在Python 3中,subprocess.Popen(..)的Windows实现使用从CreateProcessW(..)开始的正确的Windows系统调用3.0(请参阅3.0中的code)和sys.argvGetCommandLineW(..)开始使用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.argvanother 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的维护者认为这是功能请求,而不是错误修复,因此这不会改变。

有关事物为何如此的详细技术说明,请参阅其他答案。