ascii
控制字符的编号范围是0-31和127(0x00-0x1F和0x7F),共33个字符。
可显示字符编号范围是32-126(0x20-0x7E),共95个字符。
((20241112221251-kkgxrg6 "标准 ASCII 码对照表"))
UNICODE
美国人意识到他们应该提出一种标准方案来展示世界上所有语言中的所有字符,出于这个目的,Unicode 诞生了。
Unicode 源于一个很简单的想法:将全世界所有的字符包含在一个集合里,计算机只要支持这一个字符集,就能显示所有的字符,再也不会有乱码了。
它从 0 开始,为每个符号指定一个编号,这叫做”码点”(code point)。比如,码点 0 的符号就是 null(表示所有二进制位都是 0)。
U+0000 = null
上式中,U+表示紧跟在后面的十六进制数是 Unicode 的码点。
这么多符号,Unicode 不是一次性定义的,而是分区定义。每个区可以存放 65536 个(2^16)字符,称为一个平面(plane)。目前,一共有 17 个平面,也就是说,整个 Unicode 字符集的大小现在是 2^21。
最前面的 65536 个字符位,称为基本平面(缩写 BMP),它的码点范围是从 0 一直到 2^16-1,写成 16 进制就是从 U+0000 到 U+FFFF。所有最常见的字符都放在这个平面,这是 Unicode 最先定义和公布的一个平面。
剩下的字符都放在辅助平面(缩写 SMP),码点范围从 U+010000 一直到 U+10FFFF。
Unicode 只规定了每个字符的码点,到底用什么样的字节序表示这个码点,就涉及到编码方法。
Unicode 编码方案
之前提到,Unicode 没有规定字符对应的二进制码如何存储。以汉字“汉”为例,它的 Unicode 码点是 0x6c49,对应的二进制数是 110110001001001,二进制数有 15 位,这也就说明了它至少需要 2 个字节来表示。可以想象,在 Unicode 字典中往后的字符可能就需要 3 个字节或者 4 个字节,甚至更多字节来表示了。
这就导致了一些问题,计算机怎么知道你这个 2 个字节表示的是一个字符,而不是分别表示两个字符呢?这里我们可能会想到,那就取个最大的,假如 Unicode 中最大的字符用 4 字节就可以表示了,那么我们就将所有的字符都用 4 个字节来表示,不够的就往前面补 0。这样确实可以解决编码问题,但是却造成了空间的极大浪费,如果是一个英文文档,那文件大小就大出了 3 倍,这显然是无法接受的。
于是,为了较好的解决 Unicode 的编码问题, UTF-8 和 UTF-16 两种当前比较流行的编码方式诞生了。当然还有一个 UTF-32 的编码方式,也就是上述那种定长编码,字符统一使用 4 个字节,虽然看似方便,但是却不如另外两种编码方式使用广泛。
UTF-8
UTF-8 是一个非常惊艳的编码方式,漂亮的实现了对 ASCII 码的向后兼容,以保证 Unicode 可以被大众接受。
UTF-8 是目前互联网上使用最广泛的一种 Unicode 编码方式,它的最大特点就是可变长。它可以使用 1 - 4 个字节表示一个字符,根据字符的不同变换长度。编码规则如下:
对于单个字节的字符,第一位设为 0,后面的 7 位对应这个字符的 Unicode 码点。因此,对于英文中的 0 - 127 号字符,与 ASCII 码完全相同。这意味着 ASCII 码那个年代的文档用 UTF-8 编码打开完全没有问题。
对于需要使用 N 个字节来表示的字符(N > 1),第一个字节的前 N 位都设为 1,第 N + 1 位设为 0,剩余的 N - 1 个字节的前两位都设位 10,剩下的二进制位则使用这个字符的 Unicode 码点来填充。
编码规则如下:
Unicode 十六进制码点范围 UTF-8 二进制
0000 0000 - 0000 007F 0xxxxxxx
0000 0080 - 0000 07FF 110xxxxx 10xxxxxx
0000 0800 - 0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000 - 0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
根据上面编码规则对照表,进行 UTF-8 编码和解码就简单多了。下面以汉字“汉”为利,具体说明如何进行 UTF-8 编码和解码。
“汉”的 Unicode 码点是 0x6c49(110 1100 0100 1001),通过上面的对照表可以发现,0x0000 6c49 位于第三行的范围,那么得出其格式为 1110xxxx 10xxxxxx 10xxxxxx。接着,从“汉”的二进制数最后一位开始,从后向前依次填充对应格式中的 x,多出的 x 用 0 补上。这样,就得到了“汉”的 UTF-8 编码为 11100110 10110001 10001001,转换成十六进制就是 0xE6 0xB7 0x89。
解码的过程也十分简单:如果一个字节的第一位是 0 ,则说明这个字节对应一个字符;如果一个字节的第一位 1,那么连续有多少个 1,就表示该字符占用多少个字节。
UTF-16
在了解 UTF-16 编码方式之前,先了解一下另外一个概念——“平面”。
在上面的介绍中,提到了 Unicode 是一本很厚的字典,她将全世界所有的字符定义在一个集合里。这么多的字符不是一次性定义的,而是分区定义。每个区可以存放 65536 个(2^16
)字符,称为一个平面(plane)。目前,一共有 17 个(2 ^ 4+1)平面,也就是说,整个 Unicode 字符集的大小现在是 2 ^20 + 2 ^16。
最前面的 65536 个字符位,称为基本平面(简称 BMP ),它的码点范围是从 0 到 2^16-1,写成 16 进制就是从 U+0000 到 U+FFFF。所有最常见的字符都放在这个平面,这是 Unicode 最先定义和公布的一个平面。剩下的字符都放在辅助平面(简称 SMP ),码点范围从 U+010000 到 U+10FFFF。
基本了解了平面的概念后,再说回到 UTF-16。UTF-16 编码介于 UTF-32 与 UTF-8 之间,同时结合了定长和变长两种编码方法的特点。它的编码规则很简单:基本平面的字符占用 2 个字节,辅助平面的字符占用 4 个字节。也就是说,UTF-16 的编码长度要么是 2 个字节(U+0000 到 U+FFFF),要么是 4 个字节(U+010000 到 U+10FFFF)。那么问题来了,当我们遇到两个字节时,到底是把这两个字节当作一个字符还是与后面的两个字节一起当作一个字符呢?
这里有一个很巧妙的地方,在基本平面内,从 U+D800 到 U+DFFF 是一个空段,即这些码点不对应任何字符。因此,这个空段可以用来映射辅助平面的字符。
辅助平面的字符位共有 2^20 个,因此表示这些字符至少需要 20 个二进制位。UTF-16 将这 20 个二进制位分成两半,前 10 位映射在 U+D800 到 U+DBFF,称为高位(H),后 10 位映射在 U+DC00 到 U+DFFF,称为低位(L)。这意味着,一个辅助平面的字符,被拆成两个基本平面的字符表示。
因此,当我们遇到两个字节,发现它的码点在 U+D800 到 U+DBFF 之间,就可以断定,紧跟在后面的两个字节的码点,应该在 U+DC00 到 U+DFFF 之间,这四个字节必须放在一起解读。
转成utf-16的方法
①、将增补字符的码点值减去0x10000,得到一个20位长的二进制数
②、将得到的20位长二进制数拆分为高10位比特和低10位比特
③、20位长的高10位比特加上0xD800得到第一个代理码点,即高代理码点
④、20位长的低10位比特加上0xDC00得到第二个代理码点,即低代理码点
⑤、将得到的高代理码点和低代理码点组合成“代理对”,便得到了增补字符的UTF-16编码
⑥、示例:求增补平面码点值为U+10437的UTF-16编码
将0x10437减去0x10000,得到0x00437,二进制为0000 0000 0100 0011 0111
将高10位,即0000 0000 01加上0xD800(二进制为1101 1000 0000 0000),得到高代理码点为:0xD801(二进制为1101 1000 0000 0001)
将低10位,即00 0011 0111加上0xDC00(二进制为1101 1100 0000 0000),得到低代理码点为:0xDC37(二进制为1101 1100 0011 0111)
Python编码相关代码片段
>>> '汉'.encode('utf-8') # 获取汉的utf-8编码
b'\xe6\xb1\x89'
>>> ord('汉') #获取汉的unicode码点
27721
>>> hex(ord('汉')) #16进制表示unicode码点
'0x6c49'
>>> bin(ord('汉')) #二进制表示unicode码点
'0b110110001001001'
记事本TXT编码问题解释
记事本文件保存文本时,讨论一下三种编码格式,ANSI,Unicode和UTF-8,需要指出的是,此处的Unicode表示UTF-16 小端
如果是英文a,用ascii保存,查((20241112221251-kkgxrg6 "标准 ASCII 码对照表")),是0x61
如果是用Unicode保存,即为0x6100,0xFFFE为UTF16小端编码
如果是Unicode big endian保存,即为0x0061,FEFF为UTF16大端编码
如果是UTF8保存,即为0x61,EFBBBF为BOM头
中文"汉"字,unicode码是0x6c49
如果以ascii码保存,由于中文字不能以ascii保存,会以系统的编码保存,可以在cmd窗口中chcp查看默认编码,936代表简体中文(GB2312)
如果要改默认编码集,可以在下图勾选以utf-8作为默认编码
0xBABA表示“汉”字在gb2312的编码,可以在这里查看:汉字字符集编码查询;中文字符集编码:GB2312、BIG5、GBK、GB18030、Unicode
如果以unicode码保存,6c是高位,49是低位
如果是Unicode big endian保存
如果是utf8保存,0x6c49,二进制为 110110001001001
按照这条转换
0000 0800 - 0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
转换为:11100110 10110001 10001001,十六进制为0xE6B189
对于特殊字:
标签:字符,0000,字节,编码,Windows,Unicode,码点,讲透 From: https://www.cnblogs.com/hielwang/p/18544993