字符编码回顾
记得以前十多年前上网的时候,会经常碰到网页乱码,然后就会在浏览器选择一个合适的字符编码。
编码看起来有很多,但其实是早期没有统一造成的
编码是一种映射规则,字符编码作用就是将字符映射成二进制以便在计算机中存储和传输。
编码总体上我把它归为三大类:
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 | # unicode 最大字符序列为0x10FFFF |
vc 未初始化的内存用0xcc填充,值得注意的是0xcc是int3中断指令;堆上的未初始化内存用0xcd填充,同样地,可以模拟一下:
1 | s = b'\xcc\xcc\xcc\xcc\xcc\xcc' # 未初始化的内存 |