如何告诉Python sys.argv 是Unicode编码?

16

这里有一个小程序:

import sys

f = sys.argv[1]
print type(f)
print u"f=%s" % (f)

这是我的程序运行情况:
$ python x.py 'Recent/רשימת משתתפים.LNK'
<type 'str'>
Traceback (most recent call last):
  File "x.py", line 5, in <module>
    print u"f=%s" % (f)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xd7 in position 7: ordinal not in range(128)
$ 

问题在于sys.argv [1]认为它正在获取ASCII字符串,但它无法转换为Unicode。但我使用的是具有完全Unicode感知的Mac终端,因此x.py实际上正在获取Unicode字符串。如何告诉Python sys.argv []是Unicode而不是Ascii?如果失败,如何将ASCII(其中包含Unicode)转换为Unicode?常见的转换方法都不起作用。


1
可能是重复的问题?https://dev59.com/eHRA5IYBdhLWcg3wvQhh - Ben
Ben,这个问题在Unicode方面是与Mac相关的,尽管它确实涉及到一些相同的概念。 - mkelley33
5个回答

21

你看到的UnicodeDecodeError错误是因为混合使用了Unicode字符串u"f=%s"sys.argv[1]字节串:

  • 两者都是字节串:

  $ python2 -c'import sys; print "f=%s" % (sys.argv[1],)' 'Recent/רשימת משתתפים'
这将透明地从/到您的终端传递字节。它适用于任何编码。
  • 都是Unicode:

      $ python2 -c'import sys; print u"f=%s" % (sys.argv[1].decode("utf-8"),)' 'Rec..
    

    在这里,你应该将'utf-8'替换为你的终端使用的编码方式。如果终端不支持Unicode,你可以在这里使用sys.getfilesystemencoding()

  • 这两个命令都会产生相同的输出:

    f=Recent/רשימת משתתפים
    

    一般来说,您应该尽早将您认为是文本的字节串转换为 Unicode。


    实际上,我找到了问题所在。原来Python将utf-8视为ASCII而不是Unicode。尝试使用print type(u"foobar".encode('utf-8')),你会得到str而不是unicode类型。 - vy32
    5
    @vy32:'utf-8'是一种字符编码方式。在任何情况下都不代表Unicode。.encode()方法可以将Unicode字符串(文本)转换为字节串(数据)。您对Unicode的理解存在误区。请阅读http://www.joelonsoftware.com/articles/Unicode.html。 - jfs
    谢谢。实际上,我已经阅读了那篇文章。问题在于作者的断言:“编码的单个最重要事实是——没有知道它使用什么编码的字符串是没有意义的。” 在我的工作领域中,经常会出现不知道所使用编码的字符串。我们也会看到在中间更改编码的字符串。 - vy32
    1
    @vy32:如果您不知道编码,那么输入可能会存在歧义,例如"Bush hid the facts"Garbage in, garbage out - jfs
    1
    sys.getfilesystemencoding()是救命稻草。谢谢! - Alexander Revo

    5
    sys.argv = map(lambda arg: arg.decode(sys.stdout.encoding), sys.argv)
    

    或者你可以从 locale.getdefaultlocale()[1] 中选择编码。


    3

    尝试以下两种方法之一:

    f = sys.argv[1].decode('utf-8')
    

    或者:

    f = unicode(sys.argv[1], 'utf-8')
    

    3

    命令行参数以字节字符串的形式传递到Python中,使用与启动Python的shell相同的编码方式。因此,除了在应用程序中将参数转换为Unicode之外,没有其他方法将命令行参数传递到Python中作为Unicode字符串。


    3
    不用亲自转换参数,如果操作系统提供了广泛的API,Python可以使用它。在Python3中,sys.argv是一个Unicode字符串。 - jfs
    1
    @J.F Sebastian +1,使用Python 3很好!在@vy32使用的Python版本中,arg不需要在shell中转换,就像我在下面的答案中提到的那样,也不需要在代码中进行转换或升级到Python 3! - mkelley33
    @mkelley33:我的意思是,原则上你不必自己转换参数。这只是CPython2实现的一个缺陷。Python3只是一个例子,说明软件是可以为你完成这个任务的。Python3至少需要几年才能被广泛采用。我并没有说你应该使用它。 - jfs
    @J.F. Sebastian:我明白了,朋友,但是当然你应该使用Python3!我必须检查一个项目需要哪些依赖以及哪些限制可能会对考虑中的Python版本的任何开发产生不利影响。虽然仍有许多包无法在Python3上使用,但除非所涉及的项目具有某种程度的复杂性可能需要尚未准备好Python 3的依赖项,否则我不建议@pynator等待数年才使用它 :) 干杯! - mkelley33

    2
    1. sys.argv永远不会是“Unicode”,它肯定是经过编码的,但Unicode不是一种编码,而是一组代码点(数字),每个数字唯一地表示一个字符。http://www.unicode.org/standard/WhatIsUnicode.html

    2. 前往Terminal.app > Terminal > Preferences > Settings > Character encoding,并从下拉列表中选择UTF-8。

    3. 此外,Mac OS X自带的默认Python在Unicode方面有一个缺陷:默认情况下使用已弃用的UCS-2构建;请参见:http://webamused.wordpress.com/2011/01/31/building-64-bit-python-python-org-using-ucs-4-on-mac-os-x-10-6-6-snow-leopard/


    要测试#2,请转到“系统偏好设置”>“语言和文本”>“输入源”,并标记选中Unicode十六进制输入。打开交互式解释器会话,现在键入alt(option)+ 00a9。如果您看到©版权符号,则您的终端输入已使用UTF-8编码,但您仍然可能需要使用UCS-4选项构建Python。 - mkelley33
    1
    原来Python认为UTF-8是ASCII而不是Unicode。天哪,我觉得这很令人困惑。 - vy32
    1
    请注意:“每个字符都有一个唯一的数字”并不等同于“每个数字都唯一地代表一个字符”。这两者之间存在微妙的差别。代码点始终指向相同的字符,但是可以使用代码点以多种方式表示字符。例如,U+00E9é U+0065 U+0301é,即代码点不能唯一地表示一个字符,因此有Unicode规范化 http://www.unicode.org/reports/tr15/来避免Unicode字符串的二进制表示中的歧义。 - jfs
    虽然Unicode不是一种编码,但它确实包括编码(最著名的是UTF-8)。 - Eric O. Lebigot

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接