Unicode文件名传递给python subprocess.call()

10 投票
7 回答
13646 浏览
提问于 2025-04-15 21:20

我正在尝试使用unicode文件名来运行subprocess.call(),这里有个简化的问题:

n = u'c:\\windows\\notepad.exe '
f = u'c:\\temp\\nèw.txt'

subprocess.call(n + f)

这就引发了一个著名的错误:

UnicodeEncodeError: 'ascii' 编码无法编码字符 u'\xe8'

用utf-8编码会产生错误的文件名,而mbcs则把文件名当成了new.txt,没有重音符号。

我实在是看不懂这个令人困惑的话题,感觉在原地打转。我在这里发现了很多关于不同问题的答案,所以我想加入进来,自己寻求帮助。

谢谢

7 个回答

1

看起来要让这个功能正常工作,子进程的代码需要修改,使用一个宽字符版本的CreateProcess(假设这样的版本存在)。有一个PEP(Python增强提案)讨论了对文件对象进行同样修改的内容,链接在这里:http://www.python.org/dev/peps/pep-0277/。也许你可以研究一下Windows的C语言调用,并提出一个类似的修改建议给子进程。

6

如果你的文件存在,你可以使用一种叫做 短文件名(也叫8.3名字)。这个名字是专门为已经存在的文件定义的,当你把它作为参数传给不支持Unicode的程序时,应该不会出现问题。

获取短文件名的一种方法是(需要先安装 Pywin32):

import win32api
short_path = win32api.GetShortPathName(unicode_path)

另外,你也可以使用 ctypes 来实现:

import ctypes
import ctypes.wintypes

ctypes.windll.kernel32.GetShortPathNameW.argtypes = [
    ctypes.wintypes.LPCWSTR, # lpszLongPath
    ctypes.wintypes.LPWSTR, # lpszShortPath
    ctypes.wintypes.DWORD # cchBuffer
]
ctypes.windll.kernel32.GetShortPathNameW.restype = ctypes.wintypes.DWORD

buf = ctypes.create_unicode_buffer(1024) # adjust buffer size, if necessary
ctypes.windll.kernel32.GetShortPathNameW(unicode_path, buf, len(buf))

short_path = buf.value
8

我找到了一种不错的解决办法,虽然有点麻烦,但确实有效。

subprocess.call这个函数会把文本用它自己的编码方式传给终端,而这个编码可能和终端期待的编码不一样。为了让它在不同的机器上都能用,你需要在运行时知道机器的编码。

下面的代码

notepad = 'C://Notepad.exe'
subprocess.call([notepad.encode(sys.getfilesystemencoding())])

试图找出当前的编码,然后把正确的编码应用到subprocess.call中。

顺便提一下,我还发现如果你试图用当前目录来拼接一个字符串,使用

os.cwd() 

Python(或者操作系统,不太确定)会把带有重音符号的字符的目录搞乱。为了避免这种情况,我发现以下方法有效:

os.cwd().decode(sys.getfilesystemencoding())

这个方法和上面的解决方案非常相似。

希望这对你有帮助。

撰写回答