今天中午,突然想搞清楚和UTF-8的关系,于是就开始查资料。
这个问题比我想象的要复杂。 吃完午饭,我一直看到晚上九点,才想通。
以下是我的笔记,主要用来整理自己的思路。 我尽量写得通俗易懂,希望对其他朋友有用。 毕竟,字符编码是计算机技术的基石。 如果你想熟练使用计算机,你必须了解一点字符编码。
01
ASCII 码
我们知道,在计算机内部,所有信息最终都是二进制值。 每个二进制位(bit)都有0和1两种状态,因此8个二进制位可以组合成256种状态,称为一个字节(byte)。 也就是说,一个字节总共可以表示256种不同的状态,每种状态对应一个符号,从到到到就是256个符号。
20世纪60年代,美国制定了一套字符编码,对英文字符和二进制数字的关系做了统一规定。 这称为 ASCII,至今仍在使用。
ASCII码总共指定了128种字符编码。 例如,空格SPACE为32(二进制),大写字母A为65(二进制)。 这128个符号(包括32个无法打印出来的控制符号)只占用一个字节的最后7位,第一位统一设置为0。
02
非 ASCII 编码
英语是用128个符号编码的,但要表示其他语言,128个符号是不够的。 例如,在法语中,上面带有音标的字母不能用 ASCII 表示。 因此,一些欧洲国家决定使用字节中最空闲的位来编码新的符号。 例如,法语中 é 的代码是 130(二进制)。 这样,这些欧洲国家使用的编码系统最多可以表示256个符号。
然而,这里又出现了一个新问题。 不同的国家/地区有不同的字母表,因此即使它们都使用 256 符号编码,但它们代表的字母也不同。 例如,130在法语代码中代表é,但在希伯来语代码中代表字母Gimel(ג),在俄语代码中代表另一个符号。 但无论如何,在所有这些编码方式中,0–127所代表的符号都是一样的,唯一的区别就是第128–255段。
至于亚洲国家的文字,使用的符号就更多了,汉字多达十万个。 一个字节只能表示256个符号,这肯定是不够的。 必须使用多个字节来表达一个符号。 例如,简体中文常见的编码方式是用两个字节来表示一个汉字,所以理论上最多可以表示256 x 256 = 65536个符号。
中文编码的问题需要专门一篇文章来讨论,本笔记不涉及。 这里只是指出,虽然用多个字节来表示一个符号,但GB类的汉字编码与后面介绍的UTF-8无关。
03
上一节提到,世界上有很多种编码方式,同一个二进制数可以解释成不同的符号。 因此,如果要打开一个文本文件,就必须知道它的编码方式,否则如果使用错误的编码方式来解释,就会出现乱码。 为什么邮件经常出现乱码? 这是因为发送者和接收者使用不同的编码方法。
可以想象,如果有一个代码,包含了世界上所有的符号。 每个符号都被赋予唯一的代码,因此乱码问题就会消失。 顾名思义,这是所有符号的编码。
这无疑是一个庞大的集合,目前可容纳超过 100 万个符号。 每个符号的编码是不同的。 例如,U+0639代表阿拉伯字母Ain,U+0041代表英文大写字母A,U+4E25代表汉字Yan。 您可以查看特定符号对应表,或者特殊汉字对应表。
04
问题
需要注意的是,这只是一个符号集,它只规定了符号的二进制代码,但没有规定二进制代码应该如何存储。
例如,严格的汉字是十六进制数4E25,转换成15位二进制数(0101)。 也就是说这个符号的表示至少需要2个字节。 表示其他较大的符号可能需要 3 个字节或 4 个字节,甚至更多。
这里有两个严重的问题。 第一个问题是,我们如何区分它和ASCII? 计算机如何知道三个字节代表一个符号,而不是三个单独的符号? 第二个问题是我们已经知道英文字母只用一个字节来表示。 如果统一规定每个符号用三四个字节来表示,那么每个英文字母前面都要有两到三个字节为0,这对存储来说是巨大的浪费。 文本文件的大小会大两三倍,这是不可接受的。
他们的结果是: 1)多种存储方式的出现,也就是说有很多种不同的二进制格式可以用来表示。 2)长期无法推广,直到互联网的出现。
05
UTF-8
互联网的普及强烈要求统一编码方法的出现。 UTF-8 是 上使用最广泛的实现。 其他实现还有UTF-16(字符用两个字节或四个字节表示)和UTF-32(字符用四个字节表示),但在互联网上基本不使用。 再次强调,这里的连接是 UTF-8 是实现之一。
UTF-8最大的特点之一是它是一种可变长度的编码方法。 它可以用1~4个字节来表示一个符号,字节长度根据不同的符号而不同。
UTF-8的编码规则很简单,只有两条:
1)对于单字节符号,该字节的第一位设置为0,接下来的7位是该符号的代码。 所以对于英文字母来说,UTF-8编码和ASCII编码是一样的。
2)对于n字节符号(n>1),第一个字节的前n位设置为1,第n+1位设置为0,后面字节的前两位设置为10。其余未提及的二进制位均为该符号的代码。
下表总结了编码规则,其中字母 x 表示可用的编码位。
符号范围| UTF-8编码方式
(十六进制)| (二进制)
———————–+———————— – ——————
0 007F |
0 07FF | 0 07FF
0FFFF | 0FFFF
0FFFF | 0FFFF
根据上表,解读UTF-8编码就非常简单了。 如果一个字节的第一位是0,那么这个字节就是一个单独的字符; 如果第一位为1,则一行中有多少个1表示当前字符占用了多少个字节。
下面我们以汉字“颜”为例,演示如何实现UTF-8编码。
严格的是4E25(0101),根据上表可以发现4E25在第三行(0000 0800 – 0000 FFFF)的范围内,所以严格的UTF-8编码需要三个字节,即格式是。 然后从Yan的最后一位二进制数开始,从后往前填写格式中的x,多余的位补0。这样我们就得到Yan的UTF-8编码为11,转换后转为十六进制。
06
与 UTF-8 之间的转换
从上一节的例子中我们可以看出,颜码是4E25,与UTF-8编码不同。 它们之间的转换可以通过程序来实现。
平台最简单的转换方法之一就是使用内置的记事本小程序.exe。 打开文件后,单击“文件”菜单中的“另存为”命令,会弹出一个对话框,底部有一个编码下拉栏。
里面有四个选项:ANSI、big、UTF-8。
1) ANSI 是默认编码方式。 对于英文文件,为ASCII编码,对于简体中文文件,为编码(仅适用于简体中文版本,如果是繁体中文版本,则使用Big5编码)。
2)这里的编码是指.exe使用的UCS-2编码方式,即用两个字节直接存储字符编码。 该选项使用的格式。
3)Big编码对应上一个选项。 我将在下一节中解释“大”的含义。
4)UTF-8编码,也就是上一节提到的编码方式。
选择“编码方式”后,点击“保存”按钮,文件的编码方式将立即转换。
07
和大
如上一节所述,UCS-2 格式可以存储代码(最多代码点)。 以汉字颜为例,编码为4E25,需要分两个字节存储,一个字节是4E,另一个字节是25。存储时,4E在前,25在后,即大方法; 25在前面,4E在后面,就是这个方法。
这两个怪异的名字来自英国作家斯威夫特的《格列佛游记》。 书中,小人国爆发了内战。 战争的起因是人们争论鸡蛋是从大端(Big-)还是小端(-)打碎。 因为这件事,爆发了六次战争,一位皇帝丧命,另一位皇帝失去了皇位。
第一个字节在前,即“大端模式”(Big),第二个字节在前,即“小端模式”( )。
那么很自然地,就会出现一个问题:计算机如何知道某个文件的编码方式?
根据规范,每个文件的前面都会添加一个代表编码顺序的字符。 这个字符的名字是“零宽度无中断空格”(zero width no-break space),用FEFF表示。 这正好是两个字节,FF 比 FE 大 1。
如果文本文件的前两个字节是FE FF,则表示该文件采用大头方式; 如果前两个字节是FF FE,则表示该文件采用小头方式。
08
例子
下面,举个例子。
打开“记事本”程序.exe,新建一个文本文件,内容为“严”字,并以ANSI、big和UTF-8编码保存。
然后,使用文本编辑软件中的“十六进制功能”观察文件的内部编码。
1)ANSI:文件的编码是两个字节D1 CF,是颜氏的编码,这也暗示着是以大头格式存储的。
2):编码为四个字节FF FE 25 4E,其中FF FE表示以小端模式存储,真正的编码为4E25。
3)big:编码为四个字节FE FF 4E 25,其中FE FF表示大头存储。
4)UTF-8:编码为六个字节EF BB BF E4 B8 A5。 前三个字节EF BB BF表明这是UTF-8编码,后三个是严格的特定编码。 它的存储顺序和编码顺序是一致的。
该公众号团队成员由来自饿了么、阿里巴巴、蚂蚁金服的同事组成。 通过跟随顶级架构师,您可以了解最前沿的技术。
·结尾·
建筑师的巅峰
好了,今天的主题就讲到这里吧,不管如何,能帮到你我就很开心了,如果您觉得这篇文章写得不错,欢迎点赞和分享给身边的朋友。