字符编码的那些事…
字符编码
字符串编码跟数值编码有所不同,毕竟全球普及阿拉伯数字和10进制,数值的编码会相对简单些 : 每个国家的字符集都有所不同,而且在随时随刻的增加.
ASCII
ASCII 使用一个字节,精确点说是 0-127 来表示 128 个字符, 对于 英文来说足够了
ASCII值 | 控制字符 | ASCII值 | 控制字符 | ASCII值 | 控制字符 | ASCII值 | 控制字符 |
---|---|---|---|---|---|---|---|
0 | NUT | 32 | (space) | 64 | @ | 96 | 、 |
1 | SOH | 33 | ! | 65 | A | 97 | a |
2 | STX | 34 | “ | 66 | B | 98 | b |
3 | ETX | 35 | # | 67 | C | 99 | c |
4 | EOT | 36 | $ | 68 | D | 100 | d |
5 | ENQ | 37 | % | 69 | E | 101 | e |
6 | ACK | 38 | & | 70 | F | 102 | f |
7 | BEL | 39 | , | 71 | G | 103 | g |
8 | BS | 40 | ( | 72 | H | 104 | h |
9 | HT | 41 | ) | 73 | I | 105 | i |
10 | LF | 42 | * | 74 | J | 106 | j |
11 | VT | 43 | + | 75 | K | 107 | k |
12 | FF | 44 | , | 76 | L | 108 | l |
13 | CR | 45 | - | 77 | M | 109 | m |
14 | SO | 46 | . | 78 | N | 110 | n |
15 | SI | 47 | / | 79 | O | 111 | o |
16 | DLE | 48 | 0 | 80 | P | 112 | p |
17 | DCI | 49 | 1 | 81 | Q | 113 | q |
18 | DC2 | 50 | 2 | 82 | R | 114 | r |
19 | DC3 | 51 | 3 | 83 | S | 115 | s |
20 | DC4 | 52 | 4 | 84 | T | 116 | t |
21 | NAK | 53 | 5 | 85 | U | 117 | u |
22 | SYN | 54 | 6 | 86 | V | 118 | v |
23 | TB | 55 | 7 | 87 | W | 119 | w |
24 | CAN | 56 | 8 | 88 | X | 120 | x |
25 | EM | 57 | 9 | 89 | Y | 121 | y |
26 | SUB | 58 | : | 90 | Z | 122 | z |
27 | ESC | 59 | ; | 91 | [ | 123 | { |
28 | FS | 60 | < | 92 | / | 124 | |
29 | GS | 61 | = | 93 | ] | 125 | } |
30 | RS | 62 | > | 94 | ^ | 126 | ` |
31 | US | 63 | ? | 95 | _ | 127 | DEL |
特殊字符解释
特殊字符 | 解释 | 特殊字符 | 解释 | 特殊字符 | 解释 |
---|---|---|---|---|---|
NUL | 空 | VT | 垂直制表 | SYN | 空转同步 |
STX | 正文开始 | CR | 回车 | CAN | 作废 |
ETX | 正文结束 | SO | 移位输出 | EM | 纸尽 |
EOY | 传输结束 | SI | 移位输入 | SUB | 换置 |
ENQ | 询问字符 | DLE | 空格 | ESC | 换码 |
ACK | 承认 | DC1 | 设备控制1 | FS | 文字分隔符 |
BEL | 报警 | DC2 | 设备控制2 | GS | 组分隔符 |
BS | 退一格 | DC3 | 设备控制3 | RS | 记录分隔符 |
HT | 横向列表 | DC4 | 设备控制4 | US | 单元分隔符 |
LF | 换行 | NAK | 否定 | DEL | 删除 |
编码
每个国家指定了自己的编码方式 比如 中文的 Big5 GB2312 , ISO-8859-x 系列等. 这样就非常不方便了:
- GB2312 “中”编码为: D6D0
- 阿拉伯编码(ISO 8859-6)中 D6D0 表示 : ضذ
非常不方便. 因此制定了Unicode标准,用来定义字符编码.
Unicode
Unicode是一个字符集 里面规定了世界上每个字符对应的二进制代码 至于这个二进制代码如何存储则没有任何规定.
Unicode 前面的字符可能只需要一个字节表示,例如: “A”, 后面需要两个字节, 例如 “汉” 的 Unicode 是 0x6c49 (110110001001001),需要 15 位 即 2个字节存储,更后面的字符可能更长如 3个字节甚至4个字节.
如果使用不定长存储:这时候就不能确定3个字节到底是1个字符(3字节长度),还是2个字符(1字节字符+2字节字符)
如果使用定长字节存储Unicode,比如 使用4个字节 则会造成浪费.
因此出现了UTF-8 UTF-16 UTF-32 等 存储方案:
UTF-8
Unicode(0x) | 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 |
规则 :
- 单个字节字符,第一位 0,剩余 7 位对应字符的 Unicode 码点。
- 多个字节字符,需要几个字节,从头开始就有几个连续的 1,一个UTF-8编码组的其他的字节开头都是10,其余的位从右向左填充字符的 Unicode 码点
UTF-16
UTF-16使用2个字节或4个字节表示一个Unicode : 根据 Unicode 码点范围:
- U+0x00000000 - U+0x0000FFFF : 使用2个字节表示,码点即UTF-16编码
- U+0x00010000 - U+0x0010FFFF : 使用4个字节表示,首先计算 U’= Unicode -0x10000 然后表示为 20位2进制(Unicode 最大码点位是0x10FFFF 减去 0x10000 等于 0xFFFFF,因此可以表示为20位2进制): yyyy yyyy yyxx xxxx xxxx,则 UTF-16 编码就是 110110yyyyyyyyyy 110111xxxxxxxxxx
1 | H = Math.floor((c-0x10000) / 0x400)+0xD800 |
当读取时候
- UTF-16中2个字节成为一个WORD,因此看到一个WORD属于范围 DB80(1101100000000000)-DBFF,那么紧接着的WORD肯定属于范围DC00(1101110000000000)-DFFF ,这两个WORD 是一个字符.
- 如果一个WORD 小于 DB80 ,那么这个WORD 自成一个字符.
UTF-32
UTF-32编码以32位无符号整数为单位。Unicode的UTF-32编码就是其对应的32位无符号整数。
BOM
前面讲到字节序,一个字符的编码可能涉及到多个字节,因此当存储时也需要定义字节序
Unicode | UTF-16LE | UTF-16BE | UTF32-LE | UTF32-BE |
---|---|---|---|---|
0x006C49 | 49 6C | 6C 49 | 49 6C 00 00 | 00 00 6C 49 |
0x020C30 | 43 D8 30 DC | D8 43 DC 30 | 30 0C 02 00 | 00 02 0C 30 |
UTF-8 不需要字节序 : 因为 UTF-8 肯定是 110xxxxx , 1110xxxx 这样的高字节在前, 这样才能知道需要读取后面的几个字节.也可以理解为 UTF-8 只有 大端字节序
UTF-16,UTF-32 则使用 FEFF (零宽无中断空格) 这个字符作为 BOM , 在传输时候首先传输这个字符 , 用来标识 UTF-16 / 32的字节序.
FEFF 小端为 FFFE(UTF-16)和FFFE0000(UTF-32), 在Unicode中都是未定义的码位,不会出现在实际传输中,只会在开头出现.因此只要碰到 FEFF 或者 FFFE 就认为是 字节流的开头.
UTF-16/32 传输和存储时首先传输 BOM 字节 用来确定字节序,UTF-8 不需要,但是也可以为了保持统一传输 EF BB BF (FEFF)
UTF | BOM |
---|---|
UTF-8 without BOM | |
UTF-8 with BOM | EF BB BF |
UTF-16LE | FF FE |
UTF-16BE | FE FF |
UTF-32LE | FF FE 00 00 |
UTF-32BE | 00 00 FE FF |