Python 对 shell 字符串的处理

Python's handling of shell strings

我仍然不完全理解 python 的 unicode 和 str 类型是如何工作的。注意:我在 Python 2 工作,据我所知 Python 3 对同一问题采用完全不同的方法。

我所知道的:

str 是一种较老的野兽,它保存由历史迫使我们使用的太多编码方式之一编码的字符串。

unicode 是一种更标准化的表示字符串的方式,使用大量 table 所有可能的字符、表情符号、狗便便的小图片等。

decode 函数将字符串转换为 unicode,encode 则相反。

如果我在python的shell中简单地说:

>>> my_string = "some string"

那么my_string是一个str编码在ascii中的变量(而且,因为ascii是utf-8的一个子集,所以它也被编码在utf-8中) .

因此,例如,我可以通过说以下行之一将其转换为 unicode 变量:

>>> my_string.decode('ascii')
u'some string'  
>>> my_string.decode('utf-8')
u'some string'  

我不知道的:

Python 如何处理在 shell 中传递的非 ascii 字符串,知道了这一点,保存单词 "kožušček" 的正确方法是什么?

例如,我可以说

>>> s1 = 'kožušček'

在这种情况下,s1 成为一个 str 实例,我无法将其转换为 unicode:

>>> s1='kožušček'
>>> s1
'ko\x9eu\x9a\xe8ek'
>>> print s1
kožušček
>>> s1.decode('ascii')

Traceback (most recent call last):
  File "<pyshell#23>", line 1, in <module>
    s1.decode('ascii')
UnicodeDecodeError: 'ascii' codec can't decode byte 0x9e in position 2: ordinal not in range(128)

现在,当然我不能用ascii解码字符串,但是我应该使用什么编码呢?毕竟我的sys.getdefaultencoding()returnsascii! Python 在输入行 s1=kožušček 时使用哪种编码对 s1 进行编码?


我的另一个想法是

>>> s2 = u'kožušček'

但是,当我打印 s2 时,我得到了

>>> print s2
kouèek

这意味着Python丢失了整封信。有人可以给我解释一下吗?

str 个对象包含 个字节 。这些字节代表什么 Python 并没有规定。如果您生成了与 ASCII 兼容的字节,则可以将它们解码为 ASCII。如果它们包含表示 UTF-8 数据的字节,则它们可以被解码。如果它们包含表示图像的字节,那么您可以解码该信息并在某处显示图像。当您在 str 对象上使用 repr() 时,Python 将保留任何可 ASCII 打印的字节,其余字节将转换为转义序列;即使在仅 ASCII 的环境中,这也使调试此类信息变得实用。

您所在的终端或控制台 运行 交互式解释器将字节写入 stdin 流,Python 在您键入时从中读取。这些字节根据该终端或控制台的配置进行编码。

在您的情况下,您的控制台很可能将您键入的输入编码为 Windows 代码页。您需要找出确切的代码页并使用该编解码器对字节进行解码。代码页 1252 似乎适合:

>>> print 'ko\x9eu\x9a\xe8ek'.decode('cp1252')
kožušèek

当您打印那些相同的字节时,您的控制台正在读取这些字节并在已经配置的相同编解码器中解释它们。

Python 可以告诉您 认为您的控制台设置为什么编解码器;它会尝试检测 Unicode 文字的此信息,其中必须为您解码输入。它使用 locale.getpreferredencoding() function 来确定这一点,并且 sys.stdinsys.stdout 对象具有 encoding 属性;我的设置为 UTF-8:

>>> import sys
>>> sys.stdin.encoding
'UTF-8'
>>> import locale
>>> locale.getpreferredencoding()
'UTF-8'
>>> 'kožušèek'
'ko\xc5\xbeu\xc5\xa1\xc3\xa8ek'
>>> u'kožušèek'
u'ko\u017eu\u0161\xe8ek'
>>> print u'kožušèek'
kožušèek

因为我的终端已配置为 UTF-8,并且 Python 已检测到这一点,所以使用 Unicode 文字 u'...' 是可行的。数据通过Python.

自动解码

为什么你的控制台丢失了整封信我不知道;我必须访问你的控制台并做一些更多的实验,查看 print repr(s2) 的输出,并测试 0x00 和 0xFF 之间的所有字节以查看它是在控制台的输入端还是输出端。

我建议您阅读 Python 和 Unicode:

您的系统不一定使用sys.getdefaultencoding()编码;它只是在不告诉它编码的情况下进行转换时使用的默认值,如:

>>> sys.getdefaultencoding()
'ascii'
>>> unicode(s1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc5 in position 2: ordinal not in range(128)

Python 对您的系统语言环境的看法是 the locale module:

>>> import locale
>>> locale.getdefaultlocale()
('en_US', 'UTF-8')
>>> locale.getpreferredencoding()
'UTF-8'

并使用它我们可以解码字符串:

>>> u1=s1.decode(locale.getdefaultlocale()[1])
>>> u1
u'ko\u017eu\u0161\u010dek'
>>> print u1
kožušček

有可能尚未设置语言环境,'C' 语言环境就是这种情况。这可能会导致报告的编码为 None,即使默认值为 'ascii'。通常解决这个问题是 setlocale 的工作,getpreferredencoding 会自动调用它。我建议在您的程序启动时调用它一次并保存返回的值以备将来使用。用于文件名的编码也可能是另一种情况,在 sys.getfilesystemencoding() 中报告。

Python-内部默认编码由the site module设置,其中包含:

def setencoding():
    """Set the string encoding used by the Unicode implementation.  The
    default is 'ascii', but if you're willing to experiment, you can
    change this."""
    encoding = "ascii" # Default value set by _PyUnicode_Init()
    if 0:
        # Enable to support locale aware default string encodings.
        import locale
        loc = locale.getdefaultlocale()
        if loc[1]:
            encoding = loc[1]
    if 0:
        # Enable to switch off string to Unicode coercion and implicit
        # Unicode to string conversion.
        encoding = "undefined"
    if encoding != "ascii":
        # On Non-Unicode builds this will raise an AttributeError...
        sys.setdefaultencoding(encoding) # Needs Python Unicode build !

因此,如果您希望在 Python 的每个 运行 中默认设置它,您可以将第一个 if 0 更改为 if 1