所以,我试图理解在尝试将十六进制转义序列转换为整数时所涉及的数学。
所以如果我有字符串“Ô,当我做"Ã".encode('utf-8')
时,我得到一个像这个"\xc3"
的字节字符串。 ord("Ã")
是195.数学是16 * 12 + 3,这是195.事情是有道理的。
但如果我有字符“é” - 那么utf8编码的十六进制转义序列是"\xc3\xa9
- 而ord("é")
是233.这个计算是如何进行的? (a9本身就是169,所以显然不是补充)。
与此'Ĭ'.encode('utf-8')
相似。这产生了b'\xc4\xac'
。 ord('Ĭ')
是300。
谁能解释这里涉及的数学?
UTF-8是根据一些通用设计原则/约束设计的。理解这些设计原则非常重要,以便理解为什么UTF-8的编码算法就是这样。
UTF-8编码的工作方式如下:
0
位开头的单个八位字节。110
,1110
或11110
开始,其中1
位的数量表示多八位字节序列的长度,即以八位字节1110xxxx
开头的多八位字节序列是3个八位字节。10
开始。下面是一个示例:A
具有Unicode代码点U + 0041。由于它是ASCII字符,因此它将以与ASCII相同的方式编码,即作为二进制01000001
。
欧元符号€
具有Unicode代码点U + 20AC。由于它不是ASCII字符,因此需要将其编码为多个八位字节编码序列。二进制的十六进制0x20AC为10000010101100,因此需要14位来表示。
一个两个八位字节的序列看起来像这样:110xxxxx 10xxxxxx
,所以它只给我们11位。因此,我们需要一个三个八位字节的序列,如下所示:1110xxxx 10xxxxxx 10xxxxxx
。这给了我们16位,这比我们需要的多。现在,代码点的零扩展二进制表示只会被打包到x
es中:
11100010 10000010 10101100
^^^^00xx ^^xxxxxx ^^xxxxxx
这个位串的十六进制表示是0xE2 0x82 0xAC
。
注意:通过对代码点进行零扩展,可以将其编码为四个八位字节序列。这称为过长编码,UTF-8规范不允许这样做。编码必须尽可能短。
有一种名为Modified UTF-8的编码,它将ASCII NUL编码为非ASCII,而是编码为超长的多八位字节序列。这样,MUTF-8字符串可以包含ASCII NUL字符,而不包含0x00
null八位字节,因此可以由期望字符串为空终止的环境处理。
来自doc:
话(c)中
给定表示一个Unicode字符的字符串,返回表示该字符的Unicode代码点的整数。例如,ord('a')返回整数97,ord('€')(欧元符号)返回8364.这是chr()的反转。
ord
返回的是字符的Unicode代码点 - 大致是一个数字,可以让您识别Unicode中已知的大量字符中的字符。
使用UTF-8对字符进行编码时,您可以通过一系列字节来表示它,这与Unicode代码点没有直接关系。可能存在一些巧合,主要是用于以一个字节的序列表示的ASCII字符,但是对于所有更多“异国情调”的字符,这将失败。
看看The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)和the wikipedia page about UTF-8。
"é"
的ASCII编码是0xe9
,十进制基数等于233。
示例代码以方便您:
for n in range(256):
print(n,hex(n),chr(n))
所以,我以为我只是将这个问题包起来,并在收到SO的大量智慧之前发布我不理解的数学问题的答案。
第一个问题是“é”,当用utf8编码时产生"\xc3\xa9"
,ord("é")
返回233
。显然233不是195(c3的十进制表示)和169(a9的同上)的总和。发生什么了?
“é”具有相应的unicode点U+00E9
。十六进制e9
的十进制值是233.这就是ord("é")
的全部内容。
那么这最终如何成为"\xc3\xa9"
?
正如JörgWMittag解释和演示的那样,在utf8中,所有非ASCII都被“编码为多个八位字节序列”。
233的二进制表示是11101001
。因为这是非ASCII,所以需要以两个八位字节的顺序打包,根据Jörg,它将遵循以下模式:
110xxxxx 10xxxxxx
(110和10是固定的,在第一个八位字节中留有5位,在第二个八位中留有6位 - 总共11位)。
所以233的8位二进制表示符合这个模式代替xx-parts ...由于有11位可用而我们只需要8位,我们用3位000
(即00011101001
)填充8位。
^^^00011 ^^101001
(000
后跟我们的8位代表233)
11000011 10101001
(插入两个八位字节序列的233的二进制表示)
11000011
等于十六进制c3
,因为10101001
等于a9
-换句话说匹配原始序列"\xc3\xa9"
字符“Ĭ”的类似演练:
'Ĭ'.encode('utf-8')
收益b'\xc4\xac'
。 ord('Ĭ')
是300。
所以这个字符的unicode点再次是U+012C
,其小数值为300((1 * 16 * 16)+(2 * 16 * 1)+(12 * 1)) - 所以这是ord-part。
300的二进制表示再次是9位,100101100
。所以再一次需要一个两个八位字节序列的模式110xxxxx 10xxxxxx
。再次,我们用几个0
填充它,所以达到11位(00100101100
)。
^^^00100 ^^101100
(00
后跟我们的9位代表300)
11000100 10101100
(插入两个八位字节序列的300的二进制表示)。
11000100
对应c4
in hex,10101100
对ac
- 换句话说b'\xc4\xac'
。
谢谢大家帮忙解决这个问题。我学到了很多。