文章背景
本文主要是希望能够搞清楚计算机中出现的编码都代表什么意思?比如一些 ASCII 码、Unicode、UTF-8、GBK 等 都具体是什么意思?
ASCII 码
在计算机中,所有的数据在存储和运算时都要使用二进制数表示。例如,像a、b、c、d这样的52个字母(包括大写)以及0、1等数字还有一些常用的符号(例如*、#、@等)在计算机中存储时也要使用二进制数来表示,而具体用哪些二进制数字表示哪个符号,这就是编码。如果不同的计算机要想互相通信而不造成混乱,那么每台计算机就必须使用相同的编码规则,于是美国有关的标准化组织就推出了ASCII编码。
ASCII 码的发展过程如下:
第一版标准发布于 1963 年,1967 年经历了一次主要修订,最后一次更新则是在 1986 年。到目前为止总共定义了 128 个字符,其中 33 个控制字符(0 ~ 31,127),以及 95 个可显示字符。
控制字符
控制字符的用途主要是用来操控已经处理过的文字,例如我们常用的回车键、换行、制表符等,但是有很多其实到现在已经废弃掉了,因为随着时代的发展我们已经有更好的方式来实现控制。
显示字符
其余的 95 个显示字符,也是我们常用的字符,用于显示文本内容。
ASCII 码的局限性
一开始计算机只是在美国使用,128 个码还是能够满足显示需求的。但是随着计算机的普及,更多的国家语言需要在计算机上显示自己国家的语言,这时 128 个字符显然是不能够满足需求的。比如说中国的汉字,正常来说有 5w 个左右,ASCII 码是无法处理这么多文字显示的。所以在后面,各个国家也制定了自己的编码,比如中国的 GBK 编码。
GBK 编码
如上所说,等计算机发展到中国的时候,已经没有多余的显示汉字的能力。如果要正常展示汉字,128 个码是无论如何都无法做到的。因此需要对 ASCII 码进行拓展,并约定如下规范:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从 0xA1 用到 0xF7 ,后面一个字节(低字节)从 0xA1 到 0xFE ,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的 全角 字符,而原来在127号以下的那些就叫 半角 字符了。
全角以及半角字符
这里简单描述一下全角和半角的区别,半角 就是127以下的字符,相对应的 全角 就是 127 以上的字符。我们可以在 word 文档中通过设置第三方输入法来打出半角和全角字符。
GBK2312 和 GBK 的区别?
GBK2312 和 GBK 都是中文编码标准,其中 GBK2312 是 GBK 的子集,它包含了 GB2312 标准中的全部汉字和一些符号字符,共收录了汉字6763 个。GBK是汉字内码扩展规范的简称,它是 GB2312 的扩展,收录了除了 GB2312 中的汉字外的全部汉字和一些符号字符,共收录了汉字21003个。
GBK表示方法
因为是采用双字节表示方法,所以一个汉字需要两个占用两个字节。每个字节的范围是 0x81-0xFE,其中第一个字节的范围是 0x81-0xFE,第二个字节的范围是 0x40-0xFE(不包括 0x7F, 0x7F 被留空,可能是为了避免与 ASCII 码中的 DEL(删除)字符重叠。)。具体来说,如果一个字符的编码范围是 0x810xFE 和 0x400xFE,那么它就是一个GBK编码字符。
以下是 16 进制对应的 10 进制值:
0x81: -> 129
0xFE: -> 254
0x40: -> 64
0xFE: -> 254
0x7F: -> 127
举个例子:
你好 这两个字的表示方法是 0xC4 0xE3 0xBA 0xC3
如何转换一个 GBK 字符?
GBK 编码转换字符串是很简单的,可以直接通过设置TextDecoder的具体编码即可。下面代码就是如何将返回的10 进制数据转换成 GBK 格式的字符串。
js
const gbkBuf = new Uint8Array([196, 227, 186, 195, 49, 50, 51])
new TextDecoder('gbk').decode(gbkBuf) // "你好123"
字符串转换成 GBK
这个功能比较重要,同样也是项目中的应用场景。因为用户侧的服务器编码是 GBK,所以在用户输入内容时需要程序将获取到的字符串转换成 GBK 编码所对应的二进制流,这样服务器才能正确保存内容。
在网络上查找相关的文档基本能用的很少,有一个 iconv-lite 工具也只是支持 node 模块,并不支持前端工程引入。由于程序设计原因,这个转换功能并不能交由后端去实现。但是还是有大佬做出来了,具体的思路就是穷举法,将 GBK 编码表中所有的值都放置在一个数组中,这样在查询的时候直接取就行了。
参照文档如下:
Unicode 万国码
Unicode 的来历
因为不同国家采取了类似 GBK 的编码方式,导致他们之间读取内容困难。为了统一处理不同国家的编码标准,一个叫 ISO (国际标准化组织)的国际组织决定着手解决这个问题。他们采用的方法很简单:废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号的编码!他们打算叫它 Universal Multiple-Octet Coded Character Set ,简称 UCS, 俗称 UNICODE。
Unicode 表示方法
UNICODE 开始制订时,计算机的存储器容量极大地发展了,空间再也不成为问题了。于是 ISO 就直接规定必须用两个字节,也就是 16 位来统一表示所有的字符,对于 ascii 里的那些半角字符,UNICODE 保持其原编码不变,只是将其长度由原来的 8 位扩展为 16 位,而其他文化和语言的字符则全部重新统一编码。由于"半角"英文符号只需要用到低8位,所以其高 8 位永远是 0,因此这种大气的方案在保存英文文本时会多浪费一倍的空间。
Unicode 是编码吗?
它并不是编码,准确来说它是一个字符的集合,Unicode标准定义了超过130万个字符,其中大部分是为了覆盖全球使用的各种语言、符号和表情。Unicode字符范围从 U+0000 到 U+10FFFF,其中U+0000到U+FFFF范围内的字符称为基本多文种平面(Basic Multilingual Plane,BMP),共包含了大部分常用的字符。而U+10000到U+10FFFF范围内的字符则被称为补充多文种平面(Supplementary Multilingual Plane,SMP)和其他多文种平面(Additional Multilingual Plane,AMP),用于存储一些不常用的字符、象形文字和特殊符号。
如何用 Unicode 表示一个字符?
前面已经说了,Unicode 基本涵盖了全球上所有的字符,所以每一个汉字都有对应的编码来表示,并且这个编码在浏览器控制台是能够直接打印出来的。下面我们以 你好 为例,这俩字符所对应的编码分别是 \u4F60 和 \u597D 。其中 \u 是一个转义符号,用于表示这是一个 Unicode 字符,在其后是 4 个 16 进制的数字。
UTF-8
什么是utf8?
UTF-8(Unicode Transformation Format - 8-bit)是一种针对Unicode设计的可变长度字符编码,它被广泛应用于存储、传输和处理Unicode字符。
UTF-8编码的特点包括:
- 兼容ASCII:UTF-8编码是对ASCII编码的一种扩展,ASCII字符使用一个字节编码,与ASCII编码完全兼容,这意味着英文文本的UTF-8编码和ASCII编码是一样的,这样可以节省存储空间。
- 可变长度编码:UTF-8编码采用可变长度的编码方案,即不同的Unicode字符可能占用不同长度的字节。对于英文字符,UTF-8编码使用一个字节;对于其他字符(如汉字、表情符号等),UTF-8编码使用多个字节。
- Unicode兼容:UTF-8编码可以表示Unicode字符集中的所有字符,包括基本多文种平面(BMP)和补充多文种平面(SMP)中的字符,因此在全球范围内得到广泛应用。
- 自我同步性:UTF-8编码具有自我同步性,即任何字节序列的开始位置都可以确定该字节序列是一个字符的开始或是一个字符的一部分。
由于其兼容性、节省空间、自我同步性等特点,UTF-8编码已成为互联网和计算机系统中使用最广泛的字符编码之一。
长度可变性
- ASCII字符 :ASCII字符是UTF-8的子集,它们由单个字节表示。例如,英文字符 "A" 的UTF-8编码是
0x41
。 - 拉丁文字符 :一些拉丁文字符需要两个字节来表示。例如,西班牙语中的字符 "ñ" 的UTF-8编码是
0xC3 0xB1
。 - 汉字 :汉字通常需要三个字节来表示。例如,汉字 "你" 的UTF-8编码是
0xE4 0xBD 0xA0
。 - 表情符号 :一些表情符号可能需要四个字节来表示。例如,笑脸符号 "😊" 的UTF-8编码是
0xF0 0x9F 0x98 0x8A
。
如何表示一个 utf8
对于某一个字符的UTF-8编码,如果只有一个字节则其最高二进制位为 0 ;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为 1 的个数决定了其编码的位数,其余各字节均以 10 开头 。UTF-8最多可用到6个字节。
如表:
1字节 0xxxxxxx
2字节 110xxxxx 10xxxxxx
3字节 1110xxxx 10xxxxxx 10xxxxxx
4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
因此UTF-8中可以用来表示字符编码的实际位数最多有31位,即上表中x所表示的位。除去那些控制位(每字节开头的10等),这些x表示的位与UNICODE编码是一一对应的,位高低顺序也相同。
实际将UNICODE转换为UTF-8编码时应先去除高位0,然后根据所剩编码的位数决定所需最小的UTF-8编码位数。
因此那些基本ASCII字符集中的字符(UNICODE兼容ASCII)只需要一个字节的UTF-8编码(7个二进制位)便可以表示。
举例计算一个字符
还是拿汉字你 来举例子,首先在 Unicode 码表中查到其对应的值,\u4f60 ,转换成二进制就是100111101100000,上一单元可以对应出其处于第三行,也就是需要三个字节才能表示。所以需要的先得到如下编码:
1110xxxx 10xxxxxx 10xxxxxx
接着将 100111101100000 从后往前补位x,最后不足的话按照 0 来补充
可以得出如下的数据:11100100 10111101 10100000
转换成 16 进制就是:e4 bd a0