字符编码回顾

记得以前十多年前上网的时候,会经常碰到网页乱码,然后就会在浏览器选择一个合适的字符编码。

编码看起来有很多,但其实是早期没有统一造成的
编码是一种映射规则,字符编码作用就是将字符映射成二进制以便在计算机中存储和传输。
编码总体上我把它归为三大类:

1. ASCII

ascii在60年代就出现了,全称是American Standard Code for Information Interchange,它用7位表示0-127之间的128个字符。存储时是8位。重要的字符如’0’==48,A==65,a==97

2. ANSI

因为ascii不够用,就出现了ansi编码American National Standards Institute
ansi并不单独指一种具体的编码,它是这个委员会注册的国际通用集,是对ascii的扩展。使用8位表示,即扩展了128-255。ansi码不同国家和地区都不相同,是不通的,这也是以前网页乱码的原因。通常说ansi码是指本机本系统使用的编码,常见的ansi:

  • latin-1 (ISO-8859-1)

兼容ascii,编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。它用完了单个字节的所有空间。

gb2312 是简体中文的编码,兼容ascii,为什么说它是ascii扩展码?
如同拉丁编码一样,使用>=0xA0表示字符,但是每个字符固定用2个字节表示。
EUC-CN表示法中:
每个字符被分了区,比如字在16区1位,那么
第一字节为0xA0+16 = 0xB0
第二字节为0xA0+1 = 0xA1
合起来0xB0A1表示啊字

所以它是在ascii上作了扩展,使用0xA0后的空间,解码时候是每2字节解码。

GBK 向下与 gb2312 编码兼容,是gb2312的扩充,包含了古文、繁体等等

UNICODE

unicode不是一种编码方式,是一个世界通用的字符集。定义了世界上每一个字符对应的码点值,最大为0x10FFFF,显然也超过2个字节了。unicode的编码方式最常见的就是utf-8了。

utf-8

UTF-8编码是Unicode的实现方式,是一种具体的编码方法,如同gbk一样。UTF-8(8-bit Unicode Transformation Format),是可变长度的,使用1-4个字节表示一个字符。utf-8编码规则:

Unicode范围 UTF-8编码方式 字节数
0000 0000-0000 007F 0xxxxxxx 1
0000 0080-0000 07FF 110xxxxx 10xxxxxx 2
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx 3
0001 0000-0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 4

0开头,表示是单字节,
110开头,2个1表示2个字节,
1110开头,表示3个字节,
11110开头,表示4个字节,
对于多字节的都是10开头,这些称为控制字符

4字节*8=32位,减去11位控制位 = 21位数字位即
1 1111 1111 1111 1111 1111 = 0x1FFFFF 可表示的字符是可以覆盖到unicode所有的字符的

“啊”字unicode U+554A > 0800需要三个字节,
二进制为:0101 010101 001010,将其放入xxxx位,即:
11100101 10010101 10001010 即 0xE5 95 8A

utf-16

值得注意的是utf-16也是可变长编码,并不是固定的2个字节(要表示完所有的unicode,2个字节是不够的,65536 远小于 1,112,064),而utf-32(ucs-4)可以和unicode一一映射,固定4个长度。utf-16和utf-32都需要bom信息。

BOM

BOM 即byte order mark,是在文件头用来标识编码的字节顺序。字节顺序是内存中读取一段字节的约定,即高位是放在内存的高位地址(big-endian)还是低位地址(little-endian),bom现在还标识了编码,当有bom时候,程序根据bom去解码文件。

锟斤拷和烫烫烫

当有字符不能映射到unicode字符集时,unicode会用U+FFFD表示,称为REPLACEMENT CHARACTER,如果用utf8编码即为\xef\xbf\xbd,重复一次总共6个字节,用gbk编码表示,每两个字节一个汉字,\xef\xbf\xbd\xef\xbf\xbd
\xef\xbf = 锟
\xbd\xef = 斤
\xbf\xbd = 拷

1
2
3
4
5
6
7
8
9
# unicode 最大字符序列为0x10FFFF
# u=chr(0x0010FFFF+1) # ValueError: chr() arg not in range(0x110000)
# print(u)
a = b'\x80\x90'# 文件中存在这样的字符,unicode无法表示
b = a.decode("utf-8", "replace") # u+FFFD 替换
c = b.encode() #b'\xef\xbf\xbd\xef\xbf\xbd'
print(c)
d = c.decode("gbk")
print(d) # 锟斤拷

vc 未初始化的内存用0xcc填充,值得注意的是0xcc是int3中断指令;堆上的未初始化内存用0xcd填充,同样地,可以模拟一下:

1
2
3
4
5
6
s = b'\xcc\xcc\xcc\xcc\xcc\xcc' # 未初始化的内存
sd = s.decode("gbk") # 使用gbk显示内存中的内容
print(sd) # 烫烫烫
h = b'\xcd\xcd\xcd\xcd\xcd\xcd'
hd = h.decode("gbk")
print(hd) # 屯屯屯
作者

BoostMerlin

发布于

2022-12-28

更新于

2022-12-29

许可协议