前言 通过本文你能了解以下内容:
- 字符编码是什么,起什么作用?
- 字符编码的发展过程。
- 常见字符编码ASCII、Unicode、UTF-8作用与区别。
- 异常乱码的原因。
我们知道,计算机的世界里只有0和1,一切的文件、音乐、图片、视频等等都是由0和1的数据组成。那我们日常使用的输入法打出的文字,符号是如何被计算机识别的呢?复杂的世界语言、符号计算机又是如何管理区分的呢?这些问题将通过本文进行探讨。
一、什么是字符编码?
字符编码是将字符映射成其他形式的数据编译在计算机中存储和传输的映射规则。 以较为熟悉的数字来说:
十进制 |
二进制 |
十六进制 |
10 |
1010 |
0x0A |
66 |
0100 0010 |
0x42 |
这样我们熟悉的十进制数就转为了计算机熟悉的0/1。 类似的,字符也可以进行特殊的编码,转为为计算机能够熟悉的数据,以大名鼎鼎的ASCII编码为例:
二进制 |
十进制 |
十六进制 |
缩写 |
含义/作用 |
0000 0000 |
0 |
0x00 |
NULL |
空字符(Null) |
0000 0001 |
1 |
0x01 |
SOH |
标题开始 |
0000 0010 |
2 |
0x02 |
STX |
文本开始 |
0000 0011 |
3 |
0x03 |
ETX |
文本结束 |
0000 0100 |
4 |
0x04 |
EOT |
传输结束 |
0000 0101 |
5 |
0x05 |
ENQ |
请求 |
0000 0110 |
6 |
0x06 |
ACK |
确认回应 |
0000 0111 |
7 |
0x07 |
BEL |
响铃 |
0000 1000 |
8 |
0x08 |
BS |
退格 |
0000 1001 |
9 |
0x09 |
HT |
水平定位符号 |
0000 1010 |
10 |
0x0A |
LF |
换行键 |
0000 1011 |
11 |
0x0B |
VT |
垂直定位符号 |
0000 1100 |
12 |
0x0C |
FF |
换页键 |
0000 1101 |
13 |
0x0D |
CR |
CR(字符) |
0000 1110 |
14 |
0x0E |
SO |
取消变换(Shift out) |
0000 1111 |
15 |
0x0F |
SI |
启用变换(Shift in) |
0001 0000 |
16 |
0x10 |
DEL |
跳出数据通讯 |
0001 0001 |
17 |
0x11 |
DC1 |
设备控制一 (XON激活软件速度控制) |
0001 0010 |
18 |
0x12 |
DC2 |
设备控制二 |
0001 0011 |
29 |
0x13 |
DC3 |
设备控制三 (XOFF停用软件速度控制) |
0001 0100 |
20 |
0x14 |
DC4 |
设备控制四 |
0001 0101 |
21 |
0x15 |
NAK |
确认失败回应 |
0001 0110 |
22 |
0x16 |
SYN |
同步暂停 |
0001 0111 |
23 |
0x17 |
ETB |
区块传输结束 |
0001 1000 |
24 |
0x18 |
CAN |
取消 |
0001 1001 |
25 |
0x19 |
EM |
连线介质中断 |
0001 1010 |
26 |
0x1A |
SUB |
替换 |
0001 1011 |
27 |
0x1B |
ESC |
退出键 |
0001 1100 |
28 |
0x1C |
FS |
文件分隔符 |
0001 1101 |
29 |
0x1D |
GS |
组群分隔符 |
0001 1110 |
30 |
0x1E |
RS |
记录分隔符 |
0001 1111 |
31 |
0x1F |
US |
单元分割符 |
0111 1111 |
127 |
0x7F |
DEL |
Delete字符 |
二进制 |
十进制 |
十六进制 |
字符 |
0010 0000 |
32 |
0x20 |
(space) |
0010 0001 |
33 |
0x21 |
! |
0010 0010 |
34 |
0x22 |
" |
0010 0011 |
35 |
0x23 |
# |
0010 0100 |
36 |
0x24 |
$ |
0010 0101 |
37 |
0x25 |
% |
0010 0110 |
38 |
0x26 |
& |
0010 0111 |
39 |
0x27 |
' |
0010 1000 |
40 |
0x28 |
( |
0010 1001 |
41 |
0x29 |
) |
0010 1010 |
42 |
0x2A |
* |
0010 1011 |
43 |
0x2B |
+ |
0010 1100 |
44 |
0x2C |
, |
0010 1101 |
45 |
0x2D |
- |
0010 1110 |
46 |
0x2E |
. |
0010 1111 |
47 |
0x2F |
/ |
0011 0000 |
48 |
0x30 |
0 |
0011 0001 |
49 |
0x31 |
1 |
0011 0010 |
50 |
0x32 |
2 |
0011 0011 |
51 |
0x33 |
3 |
0011 0100 |
52 |
0x34 |
4 |
0011 0101 |
53 |
0x35 |
5 |
0011 0110 |
54 |
0x36 |
6 |
0011 0111 |
55 |
0x37 |
7 |
0011 1000 |
56 |
0x38 |
8 |
0011 1001 |
57 |
0x39 |
9 |
0011 1010 |
58 |
0x3A |
: |
0011 1011 |
59 |
0x3B |
; |
0011 1100 |
60 |
0x3C |
< |
0011 1101 |
61 |
0x3D |
= |
0011 1110 |
62 |
0x3E |
> |
0011 1111 |
63 |
0x3F |
? |
0100 0000 |
64 |
0x40 |
@ |
0100 0001 |
65 |
0x41 |
A |
0100 0010 |
66 |
0x42 |
B |
0100 0011 |
67 |
0x43 |
C |
0100 0100 |
68 |
0x44 |
D |
0100 0101 |
69 |
0x45 |
E |
0100 0110 |
70 |
0x46 |
F |
0100 0111 |
71 |
0x47 |
G |
0100 1000 |
72 |
0x48 |
H |
0100 1001 |
73 |
0x49 |
I |
0100 1010 |
74 |
0x4A |
J |
0100 1011 |
75 |
0x4B |
K |
0100 1100 |
76 |
0x4C |
L |
0100 1101 |
77 |
0x4D |
M |
0100 1110 |
78 |
0x4E |
N |
0100 1111 |
79 |
0x4F |
O |
0101 0000 |
80 |
0x50 |
P |
0101 0001 |
81 |
0x51 |
Q |
0101 0010 |
82 |
0x52 |
R |
0101 0011 |
83 |
0x53 |
S |
0101 0100 |
84 |
0x54 |
T |
0101 0101 |
85 |
0x55 |
U |
0101 0110 |
86 |
0x56 |
V |
0101 0111 |
87 |
0x57 |
W |
0101 1000 |
88 |
0x58 |
X |
0101 1001 |
89 |
0x59 |
Y |
0101 1010 |
90 |
0x5A |
Z |
0101 1011 |
91 |
0x5B |
[ |
0101 1100 |
92 |
0x5C |
| |
0101 1101 |
93 |
0x5D |
] |
0101 1110 |
94 |
0x5E |
^ |
0101 1111 |
95 |
0x5F |
_ |
0110 0000 |
96 |
0x60 |
` |
0110 0001 |
97 |
0x61 |
a |
0110 0010 |
98 |
0x62 |
b |
0110 0011 |
99 |
0x63 |
c |
0110 0100 |
100 |
0x64 |
d |
0110 0101 |
101 |
0x65 |
e |
0110 0110 |
102 |
0x66 |
f |
0110 0111 |
103 |
0x67 |
g |
0110 1000 |
104 |
0x68 |
h |
0110 1001 |
105 |
0x69 |
i |
0110 1010 |
106 |
0x6A |
j |
0110 1011 |
107 |
0x6B |
k |
0110 1100 |
108 |
0x6C |
l |
0110 1101 |
109 |
0x6D |
m |
0110 1110 |
110 |
0x6E |
n |
0110 1111 |
111 |
0x6F |
o |
0111 0000 |
112 |
0x70 |
p |
0111 0001 |
113 |
0x71 |
q |
0111 0010 |
114 |
0x72 |
r |
0111 0011 |
115 |
0x73 |
s |
0111 0100 |
116 |
0x74 |
t |
0111 0101 |
117 |
0x75 |
u |
0111 0110 |
118 |
0x76 |
v |
0111 0111 |
119 |
0x77 |
w |
0111 1000 |
120 |
0x78 |
x |
0111 1001 |
121 |
0x79 |
y |
0111 1010 |
122 |
0x7A |
z |
0111 1011 |
123 |
0x7B |
{ |
0111 1100 |
124 |
0x7C |
| |
0111 1101 |
125 |
0x7D |
} |
0111 1110 |
126 |
0x7E |
~ |
从上面可以发现,ASCII表共128个元素,分为了两部分(33 + 95):
- 0x00~0x31 + 0x127:用来操控已处理的文字控制符,共33个,多数已弃用。
- 0x32~0x126:可显示字符,共95个,包含数字、26个大小写英文字母,常见英文标点符号。
通过ASCII能够将常用的英文字母和符号在计算机中进行表示,但中文、日文、韩文等其他文字和符号如何能让计算机识别并显示呢?
二、字符编码的发展
2.1 ASCII的诞生
1837以前文字信息通常以书面的方式进行传递,而这之后进入现代信息时代,摩斯电报的发明将文字信息转为电子信息来传递,在影视作品中经常可以见到通讯士兵使用通讯设备滴答滴答的发送情报,而这正是使用的摩斯电报。摩斯代码的数字化并不是指转为为0/1,而是基于电流的开/关两种状态。基于“开”的时长,长“开”用"-"表示,短“开”用“.”表示,每个字母用1 ~ 4个“-”和“.”表示,后经过改良形成不同版本的摩斯代码:
再到后来,计算机的发明使用,急需字符的编码,方便人机的交互。 初代计算机的数据识别是通过纸带打孔的方式,纸带固定位置的孔位是否打孔被计算机处理为它能够识别和处理的0或1。
此时的人机交互繁琐困难,初代程序员面向打孔纸带编程,使用机器语言与计算机进行交互十分繁琐,低效,耗资源,易错。为了使这个过程变得高效,更符合人类交互特点,于是将字符进行编码,计算机对编码的字符进行解码成自己能够理解的0/1编码。于是ASCII(American Standard Code for Information Interchange美国信息交换标注代码)与1967年由美国制定了,是基于拉丁字母的编码系统,主要用于显示现代英语和其他西欧语言。所占字节为8bit,最早仅上述128个字符,后经过不断迭代扩展,后128个也被定义为其他字图案,例如:
二进制 |
十进制 |
十六进制 |
字符 |
1110 0011 |
227 |
0xE3 |
π |
1110 1100 |
236 |
0xEC |
∞ |
前128为ASCII码被称为基础ASCII码或标准ASCII码,后128个称为扩展ASCII码。
2.2 字符编码百花齐放和统一
前面提到ASCII编码主要适用英语和西欧语言,但对于其他语言却不适用,例如中文、日语、韩语、泰语、阿拉伯语等等,并且ASCII码仅8位,最多表示256字符,而中文大约共有10万多种字,需要更大容量、更灵活的编码方式,于是不同国家语系对自己的语言文字制定编码规则,例如GBK和GB2312(中国)、EUC-JP(日本)等等。
2.3 GB2312
这里以GB2312为例进行简单介绍:1980年我国推出GB2312,向上兼容了ASCII。GB2312采用双字节编码,字符编码占16bit。编码范围A1A1 ~ 0xFEFE,其中汉字编码范围0xB0A1 ~ 0xF7FE,共收录6763个汉字,虽然没有收录全部汉字,但基本满足日常生活、生产场景需要,同时GB2312还收录了包括拉丁字母、希腊字母、日文平/片假名字母,俄语西里尔字母在内的682个全角字符。 GB2312编码对所收录的字符进行了分区处理,共94个区(GB2312字符表),例如:
第16区 |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
A |
B |
B0A0 |
啊 |
阿 |
埃 |
挨 |
哎 |
唉 |
哀 |
皑 |
癌 |
蔼 |
矮 |
|
B0B0 |
鞍 |
氨 |
安 |
俺 |
按 |
暗 |
岸 |
胺 |
案 |
肮 |
昂 |
盎 |
"啊"字的GB2312编码就是0xB0A1。
这样不同国家的编码系统满足的自己国家文字编辑的需求,并且向下兼容ASCII,但问题却又出现了。试想这样的场景:你的阿拉伯朋友给你发了一封邮件,由于邮件内容采用阿拉伯字符编码,你收到邮件后GB2312由于没有收录阿拉伯语字符,导致不能解码出预期字符的问题,从而极易导致文字乱码的现象,这就是由于字符编码和解码出现了混乱。 为了避免这跨编码导致的乱码尴尬,于是Unicode闪亮登场。
2.4 Unicode
国际标准组织1991年制作统一的标准字符集Unicode,又称统一码,万国码。它为每种语言的每个字符设定了统一并且唯一的二进制编码,以满足跨语言,跨平台的文本转换、处理要求。Unicode编码范围为0x0000000 ~ 0x010FFFF,最多可以容纳17*2^16=1114112个码位。使用17个平面,每个平面有2 ^16=65536个码位。
例如“汉字”的Unicode编码为:U+6C49、U+5B57 (Unicode编码查询)
2.5 UTF-8
Unicode虽然解决了跨语言字符处理的问题,但也带来新的问题:
- 字符'a'在ASCII编码方式下编码为:0x61, 占1字节;字符'a'在Unicode编码方式下编码为:0x00000061,占4字节,这使得一本英文书以两种字符编码方式存储,文件大小差别巨大。
也就是说,虽然Unicode容量大,字符全,但编码占用空间大,使用效率低,基于节约原则,1992年便对Unicode进行了改良,诞生了UTF-8(8位元,Universal Character Set/Unicode Transformation Format)可变长度字符编码,将Unicode字符根据编码数字大小分为1~4字节,常用的英语2字节,汉字3字节,生僻字符4字节等,例如:(UTF-8编码查询)
字符 |
ASCII |
Unicode |
UTF-8 |
A |
0100 0001 |
0000 0000 0000 0000 0000 0000 0100 0001 |
0100 0001 |
中 |
0000 0000 0000 0000 0100 1110 0010 1101 |
1110 0100 1011 1000 1010 1101 |
注:
这里的Unicode是指Unicode-32,后续版本中对编码长度进度了改良。由此可以看出UTF-8节省了字符存储空间,尤其是对于英文字母。除了常见的UTF-8编码,还有UTF-16可变长字符编码、UTF-32固定长度字符编码。
三、总结
上文从字符编码发展历程的角度介绍了几种常见的字符编码的作用及其优缺点,已经乱码产生的背景和原因,通过字符编码使得全世界范围内的文字、符号得到表达和记录,还包括扩展的图案。