3.4 字符编码
内容导视:
-
字符集与字符编码
-
ASCII 字符集
-
GB2312 字符集
-
Unicode 字符集
计算机是以二进制的形式来存储数据的,我们在屏幕上看到的文字,在存储之前都被转换成了二进制(编码),在显示时也要根据二进制找到对应的字符。(解码)
字符集定义了文字和二进制的对应关系,为字符分配了唯一的编码。
如 ASCII 字符集定义的 ‘a’ 的编码是 01100001
,如果把它当作整数的补码,则为 97。
3.4.1 字符集与字符编码
字符集规定了某个文字对应的二进制数字存放方式(编码)和某串二进制数值代表了哪个文字(解码)的转换关系。
字符集只是一个规则集合的名字,对应到真实生活中,字符集就是对某种语言的称呼。例如:英语,汉语。
对于一个字符集来说,要正确编码转码一个字符需要三个关键元素:字库表(character repertoire)、编码字符集(coded character set)、字符编码(character encoding form)。
- 字库表中装着所有字符
- 编码字符集:即用一个序号(code point)来表示一个字符在字库中的位置(所使用的进制任意),如 ‘a’ 在 ASCII 字库表中的位置为 0x61。
- 字符编码:编码字符集和实际存储二进制之间的转换关系。一般来说直接将 code point 的值转为二进制直接存储。例如在 ASCII 字库中 ‘A’ 的序号为 65,转为二进制 01000001,直接存储。
3.4.2 ASCII 字符集
American Standard Code for Information Interchange:美国信息交换标准代码
ASCII 字符集是由美国人发明的,没有考虑欧洲那些扩展的拉丁字母,也没有考虑韩语和日语、汉字。
ASCII 的标准版本于 1967 年第一次发布,最后一次更新则是在 1986 年,迄今为止共收录了 128 个字符,包含了基本的拉丁字母(英文字母)、阿拉伯数字(1234567890)、标点符号(,.!
等)、特殊符号(@#$%^&
等)以及一些具有控制功能的字符(往往不会显示出来)。1 个字节表示这些符号绰绰有余。
在 ASCII 编码中,大写字母、小写字母和阿拉伯数字都是连续分布的(见下表),这给程序设计带来了很大的方便。例如要判断一个字符是否是大写字母,就可以判断该字符的 ASCII 编码值是否在 65~90 的范围内。
二进制 | 十进制 | 十六进制 | 字符/缩写 | 解释 |
---|---|---|---|---|
00000000 | 0 | 00 | NUL (NULL) | 空字符 |
00000001 | 1 | 01 | SOH (Start Of Headling) | 标题开始 |
00000010 | 2 | 02 | STX (Start Of Text) | 正文开始 |
00000011 | 3 | 03 | ETX (End Of Text) | 正文结束 |
00000100 | 4 | 04 | EOT (End Of Transmission) | 传输结束 |
00000101 | 5 | 05 | ENQ (Enquiry) | 请求 |
00000110 | 6 | 06 | ACK (Acknowledge) | 回应/响应/收到通知 |
00000111 | 7 | 07 | BEL (Bell) | 响铃 |
00001000 | 8 | 08 | BS (Backspace) | 退格 |
00001001 | 9 | 09 | HT (Horizontal Tab) | 水平制表符 |
00001010 | 10 | 0A | LF/NL(Line Feed/New Line) | 换行键 |
00001011 | 11 | 0B | VT (Vertical Tab) | 垂直制表符 |
00001100 | 12 | 0C | FF/NP (Form Feed/New Page) | 换页键 |
00001101 | 13 | 0D | CR (Carriage Return) | 回车键 |
00001110 | 14 | 0E | SO (Shift Out) | 不用切换 |
00001111 | 15 | 0F | SI (Shift In) | 启用切换 |
00010000 | 16 | 10 | DLE (Data Link Escape) | 数据链路转义 |
00010001 | 17 | 11 | DC1/XON (Device Control 1/Transmission On) | 设备控制1/传输开始 |
00010010 | 18 | 12 | DC2 (Device Control 2) | 设备控制2 |
00010011 | 19 | 13 | DC3/XOFF (Device Control 3/Transmission Off) | 设备控制3/传输中断 |
00010100 | 20 | 14 | DC4 (Device Control 4) | 设备控制4 |
00010101 | 21 | 15 | NAK (Negative Acknowledge) | 无响应/非正常响应/拒绝接收 |
00010110 | 22 | 16 | SYN (Synchronous Idle) | 同步空闲 |
00010111 | 23 | 17 | ETB (End of Transmission Block) | 传输块结束/块传输终止 |
00011000 | 24 | 18 | CAN (Cancel) | 取消 |
00011001 | 25 | 19 | EM (End of Medium) | 已到介质末端/介质存储已满/介质中断 |
00011010 | 26 | 1A | SUB (Substitute) | 替补/替换 |
00011011 | 27 | 1B | ESC (Escape) | 逃离/取消 |
00011100 | 28 | 1C | FS (File Separator) | 文件分割符 |
00011101 | 29 | 1D | GS (Group Separator) | 组分隔符/分组符 |
00011110 | 30 | 1E | RS (Record Separator) | 记录分离符 |
00011111 | 31 | 1F | US (Unit Separator) | 单元分隔符 |
00100000 | 32 | 20 | (Space) | 空格 |
00100001 | 33 | 21 | ! | |
00100010 | 34 | 22 | " | |
00100011 | 35 | 23 | # | |
00100100 | 36 | 24 | $ | |
00100101 | 37 | 25 | % | |
00100110 | 38 | 26 | & | |
00100111 | 39 | 27 | ’ | |
00101000 | 40 | 28 | ( | |
00101001 | 41 | 29 | ) | |
00101010 | 42 | 2A | * | |
00101011 | 43 | 2B | + | |
00101100 | 44 | 2C | , | |
00101101 | 45 | 2D | - | |
00101110 | 46 | 2E | . | |
00101111 | 47 | 2F | / | |
00110000 | 48 | 30 | 0 | |
00110001 | 49 | 31 | 1 | |
00110010 | 50 | 32 | 2 | |
00110011 | 51 | 33 | 3 | |
00110100 | 52 | 34 | 4 | |
00110101 | 53 | 35 | 5 | |
00110110 | 54 | 36 | 6 | |
00110111 | 55 | 37 | 7 | |
00111000 | 56 | 38 | 8 | |
00111001 | 57 | 39 | 9 | |
00111010 | 58 | 3A | : | |
00111011 | 59 | 3B | ; | |
00111100 | 60 | 3C | < | |
00111101 | 61 | 3D | = | |
00111110 | 62 | 3E | > | |
00111111 | 63 | 3F | ? | |
01000000 | 64 | 40 | @ | |
01000001 | 65 | 41 | A | |
01000010 | 66 | 42 | B | |
01000011 | 67 | 43 | C | |
01000100 | 68 | 44 | D | |
01000101 | 69 | 45 | E | |
01000110 | 70 | 46 | F | |
01000111 | 71 | 47 | G | |
01001000 | 72 | 48 | H | |
01001001 | 73 | 49 | I | |
01001010 | 74 | 4A | J | |
01001011 | 75 | 4B | K | |
01001100 | 76 | 4C | L | |
01001101 | 77 | 4D | M | |
01001110 | 78 | 4E | N | |
01001111 | 79 | 4F | O | |
01010000 | 80 | 50 | P | |
01010001 | 81 | 51 | Q | |
01010010 | 82 | 52 | R | |
01010011 | 83 | 53 | S | |
01010100 | 84 | 54 | T | |
01010101 | 85 | 55 | U | |
01010110 | 86 | 56 | V | |
01010111 | 87 | 57 | W | |
01011000 | 88 | 58 | X | |
01011001 | 89 | 59 | Y | |
01011010 | 90 | 5A | Z | |
01011011 | 91 | 5B | [ | |
01011100 | 92 | 5C | \ | |
01011101 | 93 | 5D | ] | |
01011110 | 94 | 5E | ^ | |
01011111 | 95 | 5F | _ | |
01100000 | 96 | 60 | ` | |
01100001 | 97 | 61 | a | |
01100010 | 98 | 62 | b | |
01100011 | 99 | 63 | c | |
01100100 | 100 | 64 | d | |
01100101 | 101 | 65 | e | |
01100110 | 102 | 66 | f | |
01100111 | 103 | 67 | g | |
01101000 | 104 | 68 | h | |
01101001 | 105 | 69 | i | |
01101010 | 106 | 6A | j | |
01101011 | 107 | 6B | k | |
01101100 | 108 | 6C | l | |
01101101 | 109 | 6D | m | |
01101110 | 110 | 6E | n | |
01101111 | 111 | 6F | o | |
01110000 | 112 | 70 | p | |
01110001 | 113 | 71 | q | |
01110010 | 114 | 72 | r | |
01110011 | 115 | 73 | s | |
01110100 | 116 | 74 | t | |
01110101 | 117 | 75 | u | |
01110110 | 118 | 76 | v | |
01110111 | 119 | 77 | w | |
01111000 | 120 | 78 | x | |
01111001 | 121 | 79 | y | |
01111010 | 122 | 7A | z | |
01111011 | 123 | 7B | { | |
01111100 | 124 | 7C | | | |
01111101 | 125 | 7D | } | |
01111110 | 126 | 7E | ~ | |
01111111 | 127 | 7F | DEL (Delete) | 删除 |
如 ‘a’ 存储时以 97 对应的二进制 01100001 存储。
后来欧洲人发明了 ISO8859-1。ISO8859-1 包含了 ASCII 中的所有字符(兼容),还加入了一些西欧字符。
兼容:新字符集包括原有字符集中的所有字符,且这些字符的编码在原有字符集中也是如此,如 ‘a’ 在 ASCII 的编码是 01100001,在 GBK 的编码也是 01100001,就称新字符集兼容原有字符集。
3.4.3 GB2312 字符集
ASCII 字符集中没有包含中文,国人规定了 GB2312 字符集,使用 2 个字节表示一个汉字(因为汉字太多,1 个字节最多表示 256 个字符),英文还是 1 个字节,兼容 ASCII 码。
那么存储时好存,找到字符对应的字符编码(序号转为二进制)存储即可,但对于非定长编码,读取时怎么知道是按 1 个字节读还是 2 个字节读。
如 101110011111101001100001
,凡是汉字,对应字符编码都以 1 开头,所以读取 2 个字节,1011100111111010
对应汉字 ‘国’,以 0 开头,读取 1 个字节,01100001
对应 ‘a’。
GB2312 编码范围:0xA1A1 ~ 0xFEFE,其中汉字编码范围:0xB0A1 ~ 0xF7FE。
分为 94 个区,每个区有 94 个数,点击 GB2312 编码表 查看详细。
通过序号查字符
0xA1A2,前两位是区号,后两位是区中的第几个数。
A1A2 代表 01 区的第 2 个数。(可以看出规律:01 + A0 = A1,02 + A0 = A2)
通过字符查序号
以 ‘包’ 为例,它在 16 区的第 92 个数,那么对应的十六进制的序号是多少?把它们转成 16 进制分别与 A0 相加就可以了。
16 转成十六进制是 0x10
92 转成十六进制是 0x5C
10
+ A0
= B0
5C
+ A0
= FC
即 ‘包’ 对应的序号是 0xB0FC,转成二进制是 1011000011111100。
可以查看 GB2312 中文简体字库表 直观一点。
‘、’ 对应的是 A1A0+2,即 A1A2,比较一目了然。
GBK和GB18030是后来的扩展编码,兼容 GK2312。
3.4.4 乱码的产生
不同国家有不同的编码方式,同一串二进制经不同的规则解码得到的结果很可能不一样,或者干脆显示一大堆的 ?。
如下面的例子:
使用 UTF-8 编码输入 “天下”,将文件转成 GBK 编码,会显示什么?
工具:字符集编码转换
使用 UTF-8 编码(此编码中一个汉字占 3 个字节)储存 “天下” 得到的二进制是 11100101 10100100 10101001 11100100 10111000 10001011
使用 GBK 解码,1 开头的读 2 个字节,11100101 10100100
转成十六进制是 0xE5A4,即 69 区的第 4 个数:“澶”,再读 2 个字节转 0xA9E4是 “╀”,(因为 GBK 兼容 GB2312,所有前两字可以查 GB2312 表)
最后两个字节转成的十六进制 0xB88B 不属于 GB2312 的范围,查下 GBK编码表,得 “笅”。
E5 - A0 = 0x45 = 69
A4 - A0 = 0x4 = 4
UTF-8 编码转为 GBK,“天下” -> “澶╀笅”。
所以要显示正确的结果,编码、解码需要使用同一种字符编码。
事实真的是这样吗?
创建一个 a.txt 文件,使用 UTF-8 编码,输入 “天下”。
右键此文件/属性:
两个汉字一共占用 6 个字节,说明 UTF-8 一个汉字占 3 个字节。
右键使用 Notepad++ 打开:
使用 GBK 编码读取这段二进制:
GBK 编码中,3 个汉字 6 个字节,一个汉字占 2 个字节。
3.4.5 BOM
BOM:byte order mark(标记字节顺序),因为网络传输中分为两种,大端(Big Endian)和小端(Little Endian)。[1]
在文件开头添加 BOM 标记(0xFEFF:大端、0xFFFE:小端)表明所使用的字节顺序,为 UTF-16、UTF-32 准备的。
UTF-8 不需要 BOM 表明字节顺序,但可以用 BOM 来表示编码方式,Windows 就是采用 BOM 来标记文本文件的编码方式的。
微软在 UTF-8 中使用 BOM 是因为这样可以把 UTF-8 和 ASCII 等编码区分开来,但这样的文件在 Windows 之外的操作系统里会带来问题。
不含 BOM 的 UTF-8 才是标准形式,即文件开头没有 0xEFBBBF = 0b1110 1111 10 111011 10 111111。
0b1111 111011 111111 = 0xFEFF。[2]
[1] 详见 2.3.5 解析的第 6 条注释。存储器以字节为单位存储,字节序是对于超过 1 个字节的数据而言的,如存储 0x00009A6C,先存储低字节 0x6C(小端: 0x6C9A0000),还是先存储高字节 0x00(大端:0x00009A6C)。
字节序是指编码单元内部字节与字节之间的顺序,而不是位之间的顺序,如 11011100 10110001 以大端存储:11011100 10110001 = 0xDCB1,小端存储:10110001 11011100 = 0xB1DC。
UTF-16、UTF-32 分别以 2、4 个字节为编码单元存储,是需要区分大小端的。如以 UTF-16LE 编码存储字符 “𐌀” ,对应编码是 0xD800DF00,编码单元之间的顺序确定,即 0xD800 与 0xDF00 这两个编码单元顺序确定,但编码单元内部,字节与字节之间的顺序不确定,即 0xD800 内究竟是 00 在前还是 D8 在前,没有规定;以小端存储应是低位在前,存储 0x00D8,所以 “𐌀” 的 UTF-16LE 编码为 0x00D800DF。
UTF-8 以 1 个字节为编码单元存储,不存在编码单元内的字节谁先谁后的问题,就 1 个也没法排序。
[2] 详见 3.4.9 UTF-8 的编码规则。
3.4.6 Unicode 字符集
Unicode 是为了解决传统的字符编码方案的局限而产生的,它为所有语言中的每个字符设定了统一并且唯一的序号(code point),以供全球人使用,序号也被称为代码点。
UCS-2 和 UCS-4
这个序号一般写成十六进制,在前面加上 U+。例如:“马” 的 Unicode 序号是 U+9A6C,这个无所谓,写成 0x 也行。(Unicode 码兼容 ASCII 码)
文字和序号之间的对应关系就是 UCS-2(Universal Character Set coded in 2 octets),UCS-2 使用 2 个字节表示序号,取值范围为 U+0000 ~ U+FFFF。
为了能表示更多的文字,人们又提出了 UCS-4,即用 4 个字节表示序号。它的范围为 U+00000000 ~ U+7FFFFFFF,其中 U+00000000 ~ U+0000FFFF 与 UCS-2 一样。
Unicode 本身只规定了每个字符与序号的对应关系,并没有规定这个序号在计算机中如何存储,你当然可以直接将序号转成二进制存储,但是对于 UCS-4 而言,序号直接转成二进制,每个字符需要 4 个字节,使用 ASCII 编码的地区国家,存储体积是原来的 4 倍,这是不太容易接受的,为此诞生了 UTF-8 可变长编码,英文字符还是只占一个字节。
规定存储方式的称为 UTF(Unicode Transformation Format),其中应用较多的就是 UTF-16 和 UTF-8 了。
3.4.7 UTF-32
直接存储序号转成的二进制,每个字符占用 4 个字节(包括英文字符)。比如马在 Unicode 中的序号为:U+9A6C,对应的二进制:00000000 00000000 10011010 01101100。
UTF-32 包括 UTF-32、UTF-32BE(Big Endian:大端),UTF-32LE(Little Endian:小端)。
使用不同方式存储 | 存储 “马” |
---|---|
UTF-32(Big Endian) | 00 00 FE FF 00 00 9A 6C |
UTF-32(Little Endian) | FF FE 00 00 6C 9A 00 00 |
UTF-32(不带 BOM) | 00 00 9A 6C |
没有提供 BOM,默认以大端解码。
工具网址
3.4.8 UTF-16
UTF-16 使用变长字节表示:
- 序号在 U+0000 ~ U+D7FF 的字符,使用 2 个字节表示,直接将序号转成二进制。
- 序号在 U+10000 ~ U+10FFFF 的字符,使用 4 个字节表示,编码规则如下。
0xD800 ~ 0xDFFF:0b11011 00000 000000 ~ 0b11011 11111 111111 是空,没有对应字符,如果编码以 11011 开头,那么必是读取 4 个字节,紧跟着的 5 位,如 00011,再加 1,得到序号的二进制的前缀 100。
序号 | 序号对应二进制 | 编码 |
---|---|---|
0x0000 ~ 0xD7FF、0xE000 ~ 0xFFFF | xxxxxxxx xxxxxxxx | xxxxxxxx xxxxxxxx |
0x10000 ~ 0x1FFFF | 1xxxxxx xxxxxxxxxx | 11011 00000xxxxxx 110111xxxxxxxxxx |
0x20000 ~ 0x2FFFF | 10xxxxxx xxxxxxxxxx | 11011 00001xxxxxx 110111xxxxxxxxxx |
0x30000 ~ 0x3FFFF | 11xxxxxx xxxxxxxxxx | 11011 00010xxxxxx 110111xxxxxxxxxx |
0x40000 ~ 0x4FFFF | 100xxxxxx xxxxxxxxxx | 11011 00011xxxxxx 110111xxxxxxxxxx |
0x50000 ~ 0x5FFFF | 101xxxxxx xxxxxxxxxx | 11011 00100xxxxxx 110111xxxxxxxxxx |
0x60000 ~ 0x6FFFF | 110xxxxxx xxxxxxxxxx | 11011 00101xxxxxx 110111xxxxxxxxxx |
0x70000 ~ 0x7FFFF | 111xxxxxx xxxxxxxxxx | 11011 00110xxxxxx 110111xxxxxxxxxx |
0x80000 ~ 0x8FFFF | 1000xxxxxx xxxxxxxxxx | 11011 00111xxxxxx 110111xxxxxxxxxx |
0x90000 ~ 0x9FFFF | 1001xxxxxx xxxxxxxxxx | 11011 01000xxxxxx 110111xxxxxxxxxx |
0xA0000 ~ 0xAFFFF | 1010xxxxxx xxxxxxxxxx | 11011 01001xxxxxx 110111xxxxxxxxxx |
0xB0000 ~ 0xBFFFF | 1011xxxxxx xxxxxxxxxx | 11011 01010xxxxxx 110111xxxxxxxxxx |
0xC0000 ~ 0xCFFFF | 1100xxxxxx xxxxxxxxxx | 11011 01011xxxxxx 110111xxxxxxxxxx |
0xD0000 ~ 0xDFFFF | 1101xxxxxx xxxxxxxxxx | 11011 01100xxxxxx 110111xxxxxxxxxx |
0xE0000 ~ 0xEFFFF | 1110xxxxxx xxxxxxxxxx | 11011 01101xxxxxx 110111xxxxxxxxxx |
0xF0000 ~ 0xFFFFF | 1111xxxxxx xxxxxxxxxx | 11011 01110xxxxxx 110111xxxxxxxxxx |
0x100000 ~ 0x10FFFF | 10000xxxxxx xxxxxxxxxx | 11011 01111xxxxxx 110111xxxxxxxxxx |
使用不同方式存储 | 存储 “马” |
---|---|
UTF-16(Big Endian) | FE FF 9A 6C |
UTF-16(Little Endian) | FF FE 6C 9A |
UTF-16(不带 BOM) | 9A 6C |
𐌀 的序号 U+10300 的二进制:1000000 1100000000,对应编码在表中第 2 项,将 000000 1100000000 填入 11011 00000xxxxxx 110111xxxxxxxxxx 得到编码:11011 00000000000 1101111100000000 = 0xD800DF00。
使用不同方式存储 | 存储 “𐌀” |
---|---|
UTF-16(Big Endian) | FE FF D8 00 DF 00 |
UTF-16(Little Endian) | FF FE 00 D8 00 DF |
UTF-16(不带 BOM) | D8 00 DF 00 |
例 1:UTF-16 编码 0101010101001010 对应的字符。
0b0101010101001010,读取 2 个字节,不需要做特殊处理,转成十六进制 0x554A,直接在 Unicode 字库表 中找到 ‘啊’。
例 2:UTF-16 编码 11011000001111001101111000110011 对应的字符。
编码的第 6 ~ 10 位需要加 1 得到序号的二进制前缀,如 0b01111 + 1 = 0b10000。
11011 开头,应读取 4 个字节,11011 00000111100 1101111000110011。
紧接着读取 5 位:00000,得到序号的二进制前缀:0b00000 + 0b1 = 0b1。
读取 6 位,得到 111100;
剩余位数:1101111000110011,去掉 110111,得到 1000110011;
将这三部分拼接得到:0b1 111100 1000110011 = 0x1F233,然后根据此序号在字库中查询到 “🈳”。
例 3:“🈴” 在 Unicode 的序号是 0x1F234,求它的 UTF-16LE 编码对应的十六进制。
序号的二进制前缀需要 - 1,得到编码的第 6 ~ 10 位,如 0b1100 - 1 = 0b01011。(不足 5 位前面补 0)
0x1F234 = 0b1 1111001000110100,这种 17 位的没法用 2 个字节存储,取出二进制的后 16 位:111100 1000110100,前缀为 1。
编码的开头为 11011;
紧接着的 5 位等于前缀 - 1 = 00000;
接着 6 位是取出的二进制的前 6 位:111100;
接着 6 位是固定的:110111;
最后 10 位是取出的二进制的最后 10 位:1000110100;
将其拼接得到:0b11011 00000 111100 110111 1000110100 = 0xD83C DE34。
使用小端表示,每两个字节交换顺序,再加上 BOM 表明字节顺序:0xFFFE 3CD8 34DE。
工具网址
3.4.9 UTF-8
UTF-8 使用变长字节表示:
- 序号在 U+00 ~ U+7F 的字符,编码使用 1 个字节表示,直接将序号转成二进制。
- 序号在 U+80 ~ U+7FF 的字符,使用 2 个字节表示。
- 序号在 U+800 ~ U+FFFF 的字符,使用 3 个字节表示。
- 序号在 U+10000 ~ U+10FFFF 的字符,使用 4 个字节表示。
序号 | 编码 |
---|---|
0x00 ~ 0x7F(0 ~ 127) | 0xxxxxxx |
0x80 ~ 0x7FF(128 ~ 2047) | 110xxxxx 10xxxxxx |
0x800 ~ 0xFFFF(2048 ~ 65535) | 1110xxxx 10xxxxxx 10xxxxxx |
0x10000 ~ 0x10FFFF(65536 ~ 1114111) | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
BOM:0xFEFF = 0b1111 111011 111111,填充到 1110xxxx 10xxxxxx 10xxxxxx 中,得到编码:11101111 10111011 10111111 = 0xEFBBBF。
例 1:“倩” 在 Unicode 的序号为 0x5029,求它的 UTF-8 编码对应的十六进制。
0x5029 处于 0x800 ~ 0xFFFF 范围内,对应编码 1110xxxx 10xxxxxx 10xxxxxx。
0x5029 = 0b0101000000101001,补足 16 位,不够前面补 0,截成三段,长度分别为 4、6、6 位:0101 000000 101001,然后填充到对应编码,11100101 10000000 10101001 = 0xE580A9。
带 BOM 的 UTF-8:0xEF BB BF E5 80 A9。
例 2:UTF-8 编码 111001101001011110100000,求它对应的字符。
观察前 4 位:1110,应读取 3 个字节,11100110 10010111 10100000,去掉第 1 个字节的 1110、第 2 个字节的 10、第三个字节的 10,得到 0110 010111 100000 = 0x65E0,在 Unicode 字库的 0 号平面查找到 0x65E0 对应的字符 “无”。
由于 UTF-8 的处理单元为一个字节(也就是一次处理一个字节),所以处理器在处理的时候就不需要考虑这一个字节的存储是在高位还是在低位,直接拿到这个字节进行处理就行了,因为大小端是针对大于一个字节的数的存储问题而言的。