在Windows上从Python 2.x中的命令行参数中读取Unicode字符

我希望我的Python脚本能够读取Windows中的Unicode命令行参数。 但是看起来sys.argv是一个用本地编码编码的string,而不是Unicode。 我怎样才能读完整的Unicode命令行?

示例代码: argv.py

 import sys first_arg = sys.argv[1] print first_arg print type(first_arg) print first_arg.encode("hex") print open(first_arg) 

在我的PC上设置日文代码页,我得到:

 C:\temp>argv.py "PC・ソフト申請書08.09.24.doc" PC・ソフト申請書08.09.24.doc <type 'str'> 50438145835c83748367905c90bf8f9130382e30392e32342e646f63 <open file 'PC・ソフト申請書08.09.24.doc', mode 'r' at 0x00917D90> 

这是Shift-JIS编码我相信,它“工作”的文件名。 但是,如果文件名不包含Shift-JIS字符集中的字符,则最终的“打开”调用将失败:

 C:\temp>argv.py Jörgen.txt Jorgen.txt <type 'str'> 4a6f7267656e2e747874 Traceback (most recent call last): File "C:\temp\argv.py", line 7, in <module> print open(first_arg) IOError: [Errno 2] No such file or directory: 'Jorgen.txt' 

注意 – 我正在谈论Python 2.x,而不是Python 3.0。 我发现Python 3.0提供了sys.argv作为适当的Unicode。 但是,由于缺乏第三方库支持,过渡到Python 3.0还有点早。

更新:

有几个答案说我应该解码根据任何sys.argv编码英寸这个问题是,它不是完整的Unicode,所以一些字符不能表示。

下面是使用案例给我的悲伤:我已经启用拖放到Windows资源pipe理器中的.py文件 。 我有各种各样的字符的文件名,包括一些不在系统默认代码页。 在所有情况下,我的Python脚本并没有通过sys.argv传递给它的正确的Unicode文件名,当时这些字符在当前的代码页编码中是不可表示的。

肯定有一些Windows API用完整的Unicode读取命令行(而Python 3.0是这样做的)。 我假设Python 2.x解释器没有使用它。

Solutions Collecting From Web of "在Windows上从Python 2.x中的命令行参数中读取Unicode字符"

这是一个正在寻找的解决方案,调用Windows GetCommandLineArgvW函数:
在Windows下获取带有Unicode字符的sys.argv (来自ActiveState)

但是我做了一些改变,简化了它的使用,更好地处理了某些用途。 这是我使用的:

win32_unicode_argv.py

 """ win32_unicode_argv.py Importing this will replace sys.argv with a full Unicode form. Windows only. From this site, with adaptations: http://code.activestate.com/recipes/572200/ Usage: simply import this module into a script. sys.argv is changed to be a list of Unicode strings. """ import sys def win32_unicode_argv(): """Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode strings. Versions 2.x of Python don't support Unicode in sys.argv on Windows, with the underlying Windows API instead replacing multi-byte characters with '?'. """ from ctypes import POINTER, byref, cdll, c_int, windll from ctypes.wintypes import LPCWSTR, LPWSTR GetCommandLineW = cdll.kernel32.GetCommandLineW GetCommandLineW.argtypes = [] GetCommandLineW.restype = LPCWSTR CommandLineToArgvW = windll.shell32.CommandLineToArgvW CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)] CommandLineToArgvW.restype = POINTER(LPWSTR) cmd = GetCommandLineW() argc = c_int(0) argv = CommandLineToArgvW(cmd, byref(argc)) if argc.value > 0: # Remove Python executable and commands if present start = argc.value - len(sys.argv) return [argv[i] for i in xrange(start, argc.value)] sys.argv = win32_unicode_argv() 

现在,我用它的方式只是做:

 import sys import win32_unicode_argv 

从那以后, sys.argv是一个Unicode字符串列表。 Python optparse模块似乎很乐意解析它,这太棒了。

处理编码是非常混乱的。

相信,如果你通过命令行输入数据,它会将数据编码为你的系统编码,而不是unicode。 (即使复制/粘贴应该这样做)

因此,使用系统编码解码为unicode应该是正确的:

 import sys first_arg = sys.argv[1] print first_arg print type(first_arg) first_arg_unicode = first_arg.decode(sys.getfilesystemencoding()) print first_arg_unicode print type(first_arg_unicode) f = codecs.open(first_arg_unicode, 'r', 'utf-8') unicode_text = f.read() print type(unicode_text) print unicode_text.encode(sys.getfilesystemencoding()) 

运行如下输出:提示> python myargv.py“PC·ソフト申请书08.09.24.txt”

 PC・ソフト申請書08.09.24.txt <type 'str'> <type 'unicode'> PC・ソフト申請書08.09.24.txt <type 'unicode'> ?日本語 

“PC·ソフト申请书08.09.24.txt”中包含“日本语”的文字。 (我使用Windows记事本将文件编码为utf8,我有点难以理解,为什么打印时为什么会有“?”字样?记事本如何保存utf8?)

字符串的解码方法或unicode()内置可以用来将编码转换为unicode。

 unicode_str = utf8_str.decode('utf8') unicode_str = unicode(utf8_str, 'utf8') 

另外,如果你处理编码文件,你可能想使用codecs.open()函数来代替内置的open()。 它允许您定义文件的编码,然后使用给定的编码将内容透明地解码为unicode。

所以当你调用content = codecs.open("myfile.txt", "r", "utf8").read() content将会是unicode。

codecs.open:http://docs.python.org/library/codecs.html?#codecs.open

如果我想知道什么,请告诉我。

如果你还没有,我建议阅读Joel关于Unicode和编码的文章: http : //www.joelonsoftware.com/articles/Unicode.html

尝试这个:

 import sys print repr(sys.argv[1].decode('UTF-8')) 

也许你必须用CP437CP1252替代UTF-8 您应该能够从注册表项HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage\OEMCP推断正确的编码名称

命令行可能是Windows编码。 尝试将参数解码为unicode对象:

 args = [unicode(x, "iso-8859-9") for x in sys.argv]