《转换篇》字符编码详解

发布时间 2023-09-01 11:07:27作者: Fusio

参考链接:https://blog.csdn.net/qq_43471489/article/details/123882328

前言

我们在开发中是不是经常会遇到这样的问题,比如你在VS2019中创建了一个工程,里面有C语言程序和中文注释,有一天,根据工作需要,你要把其中的一部分C文件和H文件移植到Keil工程中,当你通过复制黏贴把相应文件移植到Keil工程中,并使用MDK打开时,却发现,你移植的文件C语言程序是正常显示的,但是中文却成了一堆乱码,并且一编译各种莫名其妙的报错。这其实就有可能是你的VS2019和Keil使用了不同的编码方式,因为大部分编码兼容ASCII编码,而C语言程序是英文字符,采用了ASCII编码,所以正常显示,而中文编码就不同了,比如内存中同样的0xB0A1,使用不同编码标准去对0xB0A1解码,得到的可能就是不同的汉字。

在计算机世界中,只有0、1两种数字,不论是英文、中文还是数字,在计算机中都是以01的形式存储的。因此,要想把文字存储到计算机上,就要规定特定的01序列来表示文字。编码就是规定特定的01序列来表示文字的过程,编码表示了字符在计算机中的存储形式。

编码是什么

我们在计算机中经常见到的文字、数字、英文字母、图片、视频、音频等,这些信息在计算机中都是以二进制的形式存储的,因为内存条是电子元器件组成的,它们只有高电平低电平两种状态,即0和1两个值。实际上,我们所说的十进制、八进制等进制以及char、int、float等数据类型这些概念都是对于程序员而言的,比如十进制、十六进制只是一个数字对我们的表现形式不同,逢十进一或逢十六进一的区别;而数据类型,int、char、unsigned int等等,这些数据类型是对内存的解释不同,数据类型说明了这段内存所能表示的数据范围不同,比如char占一个字节,表示的数据范围是0~255,int是4字节,unsigned int表示无符号4字节数据。有时候在程序中我们会对变量进行类型转换,比如十进制转十六进制,又或者是char型转int型,这些转型都是对内存的解释(主要是内存的大小,数据的范围),比如char b,那么b占一个字节,我们让b=1,然后转型(int)b,其实b还是1,只不过它现在被解释为占据4个字节的内存。总之,上面这些情况,不管如何转换,同一个数据不会因为类型转换而改变内存中的实际数据,b在char类型时是00000001,转为int型后成了00000000000000000000000000000001,它还是那个1,不管是十六进制0x01还是十进制1,它在内存中都是上面的二进制。这是因为数据类型只是对内存的解释,而真正决定它们在计算机中的存储形式(是0001序列还是1110序列)的是编码,编码是指一个数据在计算机中的01序列是如何存储的。

数据类型和编码

数据类型是固定大小内存块的别名,它说明了这块内存所能表示的数据大小范围;
字符集(character set)定义了文字和二进制的对应关系,并给每个文字分配一个一对一的唯一编号;
字符编码(character Encoding)规定了文字的编号是怎么在计算机中存储的。

英文字符编码

ASCII编码

用8位二进制进行编码,用于表示控制字符、英文字符、数字字符。因为使用8位二进制编码,所以ASCII编码只能表示256个字符,编号范围为0~255。常用的ASCII码如下:

表示字符 十六进制形式 十进制形式
0~9 0x30~0x39 48~57
A~Z 0x41~0x5A 65~90
a~z 0x61~0x7A 97~122

不管是ASCII码的十六进制形式还是ASCII码的十进制形式,它都是一种解释性的概念,对内存数据的一种解释形式,用于表达给程序员看的概念,它们在计算机中的存储都是同样的二进制数,不会因为进制改变而改变。这种使用8位二进制来表示或存储字符的过程就叫做编码(一串二进制01和一个字符一一对应的过程)。这些用ASCII码表示的字符的集合叫做ASCII字符集。

在英文世界中,使用26个字母就可以拼写出全部的英文单词,每个字字母就是一个字符,所以,用8位的ASCII码就可以对整个英文世界进行编码。

中文编码

英文编码可以用字母编码来代替,这是因为所有英文单词都可以拆分成26个英文字母的组合。而中文就不一样了,中文一个字就是一个整体,只能按照一个字来编码,中文汉字成千上万,如果仅用8位ASCII码来编码,那么是明显不够的,ASCII码顶多表示256个汉字,所以就有了下面这些中文编码方式。

GB2312标准

GB2312总共覆盖了6763个常用汉字,GB2312标准把ASCII码表127号之后的扩展字符集去掉,并规定,小于127(0x7F)的编码按照ASCII标准进行解码,当出现连续两个大于127(0x7F)的编码时,这两个连续的大于0x7F的编码表示一个汉字,第一二个字节都是用0xA1~0xFE进行编码。其中,ASCII码中原有的数字字符、英文字符、标点等称为半角字符,大于0x7F的相应字符编码称为全角字符。

GB2312解码规则:当使用GB2312编码标准时,给定一串字符编码,按照字节进行检测,首先检测每个字节的大小,如果字节值小于0x7F,就用ASCII标准解码,如果连续两个字节的值都大于0x7F,就把这两个字节视为一个整体,使用GB2312标准解码。

举例:

0x61 0xB0 0xA1 0x61
a a

从第一个字节开始检测,0x61小于0x7F,用ASCII标准解码,它表示英文字符“a”,第二个字节0xB0大于0x7F,第三个字节0xA1大于0x7F,连续两个字节大于0x7F,把它们连为一体使用GB2312解码为中文字符“啊”,第四个字节0x61小于0x7F,用ASCII标准解码,它表示英文字符“a”。综上,可解码如下