在python中转义unicode字符串

时间:2015-03-24 04:24:50

标签: python python-2.7 unicode

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

print "\xF0\x9F\x8C\x80"

print u"\U0001F300"

print u"\ud83c\udf00"

如何在\ x,\ u和\ U转义之间进行翻译?我无法想象这些十六进制数是如何相等的?

4 个答案:

答案 0 :(得分:6)

第一个是字节串:

>>> "\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 :(得分:3)

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

您的第二个字符串是一个带有单个代码点{Unicode}的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个字符串最终在输出上产生相同的字节序列,生成相同的字符。

答案 2 :(得分:2)

请参阅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位)字符代码点,第二个代码为8字节(32位)代码点提供八个十六进制数字。字节字符串仅支持编码文本和其他形式的基于字节的数据的十六进制转义

答案 3 :(得分: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对这一个角色进行更多编码。