【问题标题】:Python's handling of shell stringsPython 对 shell 字符串的处理
【发布时间】:2015-10-21 11:09:14
【问题描述】:

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

我所知道的

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

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

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() 返回ascii!当输入s1=kožušček 行时,Python 使用哪种编码来编码s1


我的另一个想法是说

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

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

>>> print s2
kouèek

这意味着 Python 丢失了一个完整的字母。谁能给我解释一下?

【问题讨论】:

标签: string unicode encoding utf-8 python-2.x


【解决方案1】:

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 可以告诉您 it 认为您的控制台设置为什么编解码器;它尝试检测 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:

【讨论】:

  • 是cp1250,但谢谢!不过,第二个答案如何适应这个?为什么u'kožušček'会搞得这么一团糟?
  • 感谢您提供的链接,我阅读了大部分链接(尤其是 NO EXCUSES 链接),我遇到的问题只是 shell -> 字符串部分。现在更清楚了。谢谢。
  • 如果这是在 Windows 命令提示符中,那么知道该控制台在 Unicode 方面存在巨大问题,至少在 Python 与其交互的方式以及 Microsoft 所做的默认字体选择方面。
  • @5xum:您是否正在使用 IDLE? IDLE uses latin-1 instead of your locale encoding to decode Unicode literals 有一个错误。 Python 2 的其他部分可能存在类似的错误,即错误也可能发生在读取 Unicode 文字部分。如果你运行:print u'ko\u017eu\u0161\xe8ek',你会看到什么? (注意:文字中没有非ASCII字符)。注意:cp1250(很可能)不是您的控制台编码(Windows 使用不同的范围)。 Use WriteConsoleW(), to print Unicode
【解决方案2】:

您的系统不一定使用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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-12
    • 2014-12-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多