【问题标题】:Escaping unicode strings in python在python中转义unicode字符串
【发布时间】:2015-05-27 07:32:29
【问题描述】:

在 python 中,这三个命令打印相同的表情符号:

print "\xF0\x9F\x8C\x80"
????
print u"\U0001F300"
????
print u"\ud83c\udf00"
????

如何在 \x、\u 和 \U 转义之间进行转换?我不知道这些十六进制数字是如何等价的?

【问题讨论】:

  • 我的意思是,我不明白 \xF0\x9F 和 0001 和 d83c 是同一个数字吗?
  • 他们不是同一个数字!它们是同一代码点的不同编码
  • u"\U0001F300"==u"\ud83c\udf00" 是 Python 2 的“狭窄”构建的一个缺点,通常不是你想要依赖的东西。如果您打算使用表情符号字符,请始终使用 \U 形式。

标签: python python-2.7 unicode


【解决方案1】:

其他答案描述了如何在 Python 2.x 中将 Unicode 字符编码或嵌入为文字。让我回答您的更多元问题,“对我来说,\xF0\x9F 和 0001 和 d83c 是同一个数字吗?”

分配给每个Unicode "code point"——粗略地说,分配给每个“字符”的数字——可以用多种方式编码。这类似于整数可以通过多种方式编码:

  • 0b1100100(二进制,基数 2)
  • 0144(八进制,以 8 为底)
  • 100(十进制,以 10 为底)
  • 0x64(十六进制,以 16 为基数)

这些都是 相同 值,十进制 100,具有不同的编码。以下是 Python 中的真表达式:

0b1100100 == 0144 == 100 == 0x64

Unicode's encodings 稍微复杂一点,但原理是一样的。仅仅因为这些值看起来不一样并不意味着它们不表示相同的值。在 Python 2 中:

u'\ud83c\udf00' == u'\U0001F300' == "\xF0\x9F\x8C\x80".decode("utf-8")

Python 3 改变了字符串字面量的规则,但仍然是这样:

u'\U0001F300' == b"\xF0\x9F\x8C\x80".decode("utf-8") 

需要显式的b(字节前缀)。 u(Unicode 前缀)是可选的,因为所有字符串都被认为包含 Unicode,而 u 仅在 3.3 及更高版本中允许使用。多字节组合字符......好吧,反正它们不是那么漂亮,是吗?

所以您介绍了 Unicode CYCLONE 代码点的各种编码,其他答案显示了在代码点之间移动的一些方法。 See this 获取该字符的更多编码。

【讨论】:

  • 在我的 python (v2.7.8) 上,u'\ud83c\udf00' == u'\U0001F300' 是不正确的
  • @wim 什么平台?我只是re-verified it on Mac, under Python 2.7.9。在 Python 3 上不是这样,但在 Python 2.5-2.7.9 上是这样。
  • 在 Linux (ubuntu 14.10) 上
  • 顺便说一句,我不同意你所说的“Python 3 更复杂”的说法,在这种情况下它不那么复杂。是 python 2 具有代理对的这种奇怪的边缘情况(请参阅 Mark Ransom 对此的回答)
  • @wim 好吧,Python 3 不会自动合并 u'\ud83c\udf00' UTF-16 对,而 Python 2 和其他语言(如 Java 和 C#)会这样做。 Python 3 让一些事情变得更简单。这通常会更好,因为这些事情是人们需要用 Unicode 完成的主流事情。但其他事情变得更难了,因为字符串和字节不统一。
【解决方案2】:

您的第一个字符串是字节字符串。它打印单个表情符号字符的事实意味着您的控制台配置为打印UTF-8 编码字符。

您的第二个字符串是具有单个代码点U+1F300 的 Unicode 字符串。 \U 指定接下来的 8 个十六进制数字应解释为代码点。

第三个字符串利用了 Unicode 字符串在 Python 2 中存储方式的一个怪癖。您已经给出了两个 UTF-16 实体,它们一起形成了与前一个字符串相同的单个代码点 U+1F300。每个 \u 需要 4 个以下十六进制数字。单独地,这些字符不是有效的 Unicode,但是因为 Python 2 在内部将其 Unicode 存储为 UTF-16,所以它可以解决。在 Python 3 中这是无效的。

当您打印出 Unicode 字符串并且您的控制台编码已知为 UTF-8 时,Unicode 字符串被编码为 UTF-8 字节。因此,这 3 个字符串最终会在输出中生成相同的字节序列,生成相同的字符。

【讨论】:

    【解决方案3】:

    第一个是字节串:

    >>> "\xF0\x9F\x8C\x80".decode('utf8')
    u'\U0001f300'
    

    u"\ud83c\udf00" 一个是 UTF16 版本(四位 unicode 转义)

    u"\U0001F300" 是代码点的实际索引。


    但是这些数字有什么关系呢?这是个难题。它是由编码定义的,没有明显的关系。为了给你一个想法,这里是一个“手动”将索引 0x1F300 处的代码点编码为 UTF-8 的示例:

    旋风字符 ? 的索引为 0x1f300,位于 0x00010000 - 0x001FFFFF 范围内。这个范围的模板是:

    11110... 10...... 10...... 10......
    

    用代码点的二进制表示填充点。我不能告诉你为什么模板看起来像那样,它只是 utf-8 定义。

    这是我们代码点的二进制表示:

    >>> u'?'
    u'\U0001f300'
    >>> unichr(0x1f300)
    u'\U0001f300'
    >>> bin(0x1f300)
    '0b11111001100000000'
    

    因此,如果我们采用字符串模板并像这样填充它(使用一些前导零,因为模板中的插槽比我们的数字中的有效数字多),我们会得到:

    11110... 10...... 10...... 10......
    11110000 10011111 10001100 10000000
    

    现在让我们将其转换回十六进制

    >>> 0b11110000100111111000110010000000
    4036988032
    >>> hex(4036988032)
    '0xf09f8c80'
    

    然后你就有了代码点的 UTF8 表示。

    对于 UTF16,您的代码点有一个 similar magic recipe:从索引中减去 0x10000,然后我们用零填充以获得 20 位二进制表示。前十位添加到 0xD800 以给出第一个 16 位代码单元。最后十位添加到 0xDC00 以提供第二个 16 位代码单元。

    >>> bin(0x1f300 - 0x10000)[2:].rjust(20, '0')
    '00001111001100000000'
    >>> _[:10], _[10:]
    ('0000111100', '1100000000')
    >>> hex(0b0000111100 + 0xd800)
    '0xd83c'
    >>> hex(0b1100000000 + 0xdc00)
    '0xdf00'
    

    还有您的 UTF 16 版本,即带有小写 \u 转义的版本。

    正如您可能理解的那样,这些表示中的十六进制数字之间可能没有明显的数字关系,它们只是同一代码点的不同编码。

    【讨论】:

    • 四个 1 表示编码长度为 4 个字节。 10 开始每个连续字节。
    • 嗯,是的,因为 utf8 是可变宽度编码。谢谢@IgnacioVazquez-Abrams :)
    【解决方案4】:

    Unicode Literals in Python Source Code

    在 Python 源代码中,Unicode 文字被写为前缀为“u”或“U”字符的字符串:u'abcdefghijk'。可以使用\u 转义序列编写特定的代码点,后跟四个十六进制数字给出代码点。 \U 转义序列类似,但需要 8 个十六进制数字,而不是 4 个

    In [1]: "\xF0\x9F\x8C\x80".decode('utf-8')
    Out[1]: u'\U0001f300'
    
    In [2]: u'\U0001F300'.encode('utf-8')
    Out[2]: '\xf0\x9f\x8c\x80'
    
    In [3]: u'\ud83c\udf00'.encode('utf-8')
    Out[3]: '\xf0\x9f\x8c\x80'
    

    \uhhhh     --> Unicode character with 16-bit hex value  
    \Uhhhhhhhh --> Unicode character with 32-bit hex value
    

    在 Unicode 转义中,第一种形式给出四个十六进制数字 编码一个 2 字节(16 位)字符代码点,第二个为 4 字节(32 位)代码点提供八个十六进制数字。字节字符串仅支持编码文本和其他形式的基于字节的数据的十六进制转义

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-03
      • 2010-11-02
      • 2011-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-27
      • 2017-11-28
      相关资源
      最近更新 更多