如何根据字节序列计算整数

问题描述 投票:0回答:4

所以,我试图理解在尝试将十六进制转义序列转换为整数时所涉及的数学。

所以如果我有字符串“Ô,当我做"Ã".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。

谁能解释这里涉及的数学?

python-3.x utf-8 hex
4个回答
2
投票

UTF-8是根据一些通用设计原则/约束设计的。理解这些设计原则非常重要,以便理解为什么UTF-8的编码算法就是这样。

  1. 向后兼容ASCII:每个ASCII字符在ASCII和UTF-8中应具有相同的编码。
  2. 非ASCII字符的可检测性:不是ASCII字符的有效编码的八位字节应出现在非ASCII字符的多八位字节编码序列中。
  3. 长度编码:多八位字节编码序列的长度应该在第一个八位字节中编码,这样我们就可以在读取整个多八位字节编码序列之前知道它的长度。而且,人们很容易确定多八位字节编码序列的长度。
  4. 后备/自动检测:一种流行的8位编码(例如ISO8859-15,Windows-1252)中的文本极不可能包含有效的UTF-8多八位字节编码序列,因此这样的编码可以是易于检测,反之亦然。
  5. 自同步:您可以在UTF-8流的中间任何地方开始解码,它将最多带您到下一个ASCII字符或下一个多八位字节编码序列的开始,以便能够开始解码有效字符。如果您可以在流中向后导航,则最多需要备份3个八位字节才能找到有效的起点。
  6. 排序顺序:按八位字节对UTF-8流进行排序将自动通过代码点生成排序顺序,而无需对流进行解码。

UTF-8编码的工作方式如下:

  • 任何ASCII字符的编码方式与ASCII相同,只是以0位开头的单个八位字节。
  • 任何非ASCII字符都被编码为多个八位字节序列。
  • 多八位字节编码序列的第一个八位字节以位模式110111011110开始,其中1位的数量表示多八位字节序列的长度,即以八位字节1110xxxx开头的多八位字节序列是3个八位字节。
  • 作为多八位字节序列的一部分的任何其他八位字节以位模式10开始。
  • Unicode码点被编码到多八位字节编码序列的非固定位中。

下面是一个示例:A具有Unicode代码点U + 0041。由于它是ASCII字符,因此它将以与ASCII相同的方式编码,即作为二进制01000001

欧元符号具有Unicode代码点U + 20AC。由于它不是ASCII字符,因此需要将其编码为多个八位字节编码序列。二进制的十六进制0x20AC为10000010101100,因此需要14位来表示。

一个两个八位字节的序列看起来像这样:110xxxxx 10xxxxxx,所以它只给我们11位。因此,我们需要一个三个八位字节的序列,如下所示:1110xxxx 10xxxxxx 10xxxxxx。这给了我们16位,这比我们需要的多。现在,代码点的零扩展二进制表示只会被打包到xes中:

11100010 10000010 10101100
^^^^00xx ^^xxxxxx ^^xxxxxx

这个位串的十六进制表示是0xE2 0x82 0xAC

注意:通过对代码点进行零扩展,可以将其编码为四个八位字节序列。这称为过长编码,UTF-8规范不允许这样做。编码必须尽可能短。

有一种名为Modified UTF-8的编码,它将ASCII NUL编码为非ASCII,而是编码为超长的多八位字节序列。这样,MUTF-8字符串可以包含ASCII NUL字符,而不包含0x00 null八位字节,因此可以由期望字符串为空终止的环境处理。


2
投票

来自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


0
投票

"é"的ASCII编码是0xe9,十进制基数等于233。

示例代码以方便您:

for n in range(256):
    print(n,hex(n),chr(n))

0
投票

所以,我以为我只是将这个问题包起来,并在收到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 ^^101001000后跟我们的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 ^^10110000后跟我们的9位代表300)

11000100 10101100(插入两个八位字节序列的300的二进制表示)。

11000100对应c4in hex,10101100ac - 换句话说b'\xc4\xac'

谢谢大家帮忙解决这个问题。我学到了很多。

© www.soinside.com 2019 - 2024. All rights reserved.