Linux入职基础-2.10_字符编码入门(乱码不用再提问)

本文从字符编码的基础概念出发,详细介绍了ASCII、GB2312、GBK、GB18030、Unicode等字符集的发展历程,并对比了UTF-8、UTF-16和UTF-32的特点。通过实例讲解,帮助读者理解不同编码方式的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

字符编码入门(乱码不用再提问)

一、什么是字符

人们常把诸如文字、标点符号、图形符号、数字等统称为字符。“A”是一个字符,“B” 是一个字符,“¥”是一个字符,“中”是一个汉字字符,它没有固定的形状(可能是一个物体的形),字符仅仅代表一个符号。而由字符组成的集合则成为字符集。字符集由于包含字符的多少与异同而形成了各种不同的字符集。

我们人类能够认识与理解字符的含义,但是计算机说“我非人类,不认识你们这些圆圆圈圈的怪怪形状,我只能识别诸如0101这样的二进制数”。

在计算机内部,所有的信息最终都表示为一个二进制位的串,比如,一个8位的串亦被称为一个字节(byte)。每一个二进制位(bit)有0和1两种状态,因此,从0000000到11111111,这八个二进制位的串就可以组合出256种状态。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号。

我们人类文化的传承希望得到计算机这个朋友的帮助,为了与其进行交互,于是,人们就将使用的字符按一定规则转换为二进制数(常用16迚制来表示)。

举例:

把大写字母A对应0x41(65)  二进制01000001

把小写字母a 对应0x61(97)

人们去指定哪些二进制数,对应着哪些字符,定义了一套规则来建立对应的映射关系,这就叫字符编码

二、文件编码发展史

2.1、ASCII编码

开始计算机只在美国用,这字符编码是设计给美国人用的,显示基本的英文字符,在当时,各个厂家或公司(比如IBM)都有自己编码方法(EBCDIC、ASCII等),后来逐渐统一使用ASCII编码。

ASCII有哪些规则:

编号从0开始的32种状态分别规定了特殊的用途,就把这些0x20以下的字节状态称为"控制码",终端、打印机遇上约定好的这些字节被传过来时,就要做一些约定的动作:

举例:

遇上00x10, 终端就换行,遇上0x07, 终端就向人们嘟嘟叫,遇上0x1b, 打印机就打印反白的字,或者终端就用彩色显示字母。遇到0x20打印机就空格"SPACE"(二进制00100000)。

美国人又把所有的空 格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号。于是大家都把这个方案叫做 ANSI 的"Ascii"编码。

ASCII编码一共规定了128个字符的编码,只占用了一个字节的后面7位,最前面的1位统一规定为0。

2.2、ISO-8859-1(ASCII编码扩展)

计算机技术从美国来到世界各地,欧洲人们(比如法、德)开始使用计算机,比如,在法语中,字母上方有注音符号,它就无法用ASCII码表示。于是,为了可以在计算机保存他们的文字,一些欧洲国家就决定,利用字节中闲置的最高位(采用127号之后的空位)编入新的符号(还加入了很多画表格时需要用下到的横线、竖线、交叉等形状),一直把序号 编到了最后一个状态255。

举例:

法语中的é的编码为130(二进制10000010)。

这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。从128到255这一页的字符的集合被称"扩展字符集"。

ASCII编码是一个7位的容器,ISO-8859-1编码则是一个8位的容器。

ISO-8859-1还有个别名叫Latin1,有些环境下写作Latin-1(比如,在Linux系统中vim命令判断文件编码格式时常见,MySQL数据库默认编码是Latin1)。

2.3、 GB2312

随着时间的推移,到我们中国人使用计算机时有上万个汉字要保存,可是这时已经没有可以利用的字节状态来表示汉字了(欧洲人全用完了!),因为ISO-8859-1编码范围使用了单字节内的所有空间,这下该如何是好?

但这难不倒智慧的中国人,规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到 0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的 字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。 于是就把这种汉字方案叫做 "GB2312"。

不难看出,GB2312 是对 ASCII 的中文扩展。

2.4、GBK

中国地大物博,发现更多文字需要存储,GB2312编码还是不够用,咋办?

于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 GBK 标准,GBK 包括了 GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。

2.5、GB18030

我们的文化博大精深,传承上下5千年的历史,上古流传很多文字也需要存储,又加了几千个字,GBK编码当然不够用了。于是乎,我们再扩展,GBK 扩成了 GB18030。

扩展三次也够累的,小结下吧:

现在,你对GB系列编码即从GB2312、GBK到GB18030。大致应该有个大概了解,三个汉字编码方案所能表示的字符越来越多,但是它们均有一个最大的特点:就是用两字节来表示一个汉字字符和一字节长的英文字符并存于同一套编码方案中。

2.6、Unicode编码与互联网时代

互联网时代来临,大家同处一地球村,这里又出现了新的问题,不同的国家有不同的字母,比如,130在法语编码中代表了é,在希伯来语编码中却代表了字母Gimel (?),在俄语编码中又会代表另一个符号。

这就说明同一个二进制数字(10000010)可以被解释成不同的符号。举例一个我们日常遇到的情况

发信人(法国人),编辑一个文本文件,内容里有[ é ]这个字符(二进制10000010),收信人(犹太人)就必须知道该文本文件它的编码方式,如果是匿名信,不知来自哪个国家的,他就尝试用自己的编码方式解读,这时计算机把二进制10000010解读出字母Gimel (?),出现了乱码,一大堆他不懂含义的符合。这就是为什么电子邮件常常出现乱码的原因!

如果中国人用汉字的编码(两字节)写信给法国人,通过计算机传送后,法国人用自己的编码方式打开,那就更乱得一塌糊涂了!但是不管怎样,0--127表示的符号是一样的,不一样的是128--255的这一段。

地球村民就想办法了,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的二进制来表示,那么乱码问题就会消失。这就是Unicode。

随着时间推移,互联网的更普及,人们就强烈要求出现一种统一的编码方式。这时UTF-8呼吁而出,它是在互联网上使用最广的一种Unicode的实现方式之一。

UTF-8,字符用1~4个字节表示

UTF-16,字符用两个字节表示

UTF32,字符用四个字节表示

总之:UNICODE字符集有多个编码方式,分别是UTF-8,UTF-16和UTF-32。下一节我们对比它们的优缺点!

2.7、常用的UTF-8

UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

它的规则很简单,只有二条:

①对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。

②对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

上述先介绍了字符、字符集概念,后再介绍了ASCII、GB2312、GBK、GB18030、UNICODE等字符集的大概发展由来。接下来通过实例来加深大家对上述概念的理解。

三、UTF-8,UTF-16和UTF-32有缺点对比

UTF-8、UTF-16和UTF-32都可以表示有效编码空间 (U+000000-U+10FFFF) 内的所有Unicode字符。

UTF-8:编码时采用变长字节 (1 ASCII, 2 希腊字母, 3 汉字, 4 平面符号)来表示,存储效率比较高,适用于拉丁字符较多的场合以节省空间。

UTF-16:每个字符只占2个字节。对于大多数非拉丁字符(如中文和日文)来说,所需存储空间最小。因此WindowsNT内核是Unicode(UTF-16),采用UTF-16编码在调用系统API时无需转换,处理速度也比较快。

UTF-32:每个字符采用4字节编码,一方面处理速度比较快,但另一方面也浪费了大量空间,影响传输速度,因而很少使用。

采用UTF-16和UTF-32会有Big Endian和Little Endian之分(这是两种字节顺序,会在第3节详细介绍),而UTF-8则没有字节顺序问题,UTF-8在网络传输时, 即使错了一个字节,不影响其他字节,而双字节(比如GB系列、UTF-16)只要一个错了,其他也会错了。

所以,UTF-8编码文件适合网络传输和通信。这也是为何近年来UTF-8编码的网页文件在网站上越来越流行的原因!

三、实例讲解,

3.1文件编码的转换

在Windows平台下,有一个最简单的转化方法,就是使用内置的记事本小程序Notepad.exe。打开文件后,输入内容Aa123中国严

点击"文件"菜单中的"另存为"命令,会跳出一个对话框,在最底部有一个"编码"的下拉条。

里面有四个选项:ANSI,Unicode,Unicode big endian 和 UTF-8,选择完"编码方式"后,点击"保存"按钮,文件的编码方式就立刻转换好了。保存四种不同编码格式文件,如下:

现在我们用二进制编辑工具(UltraEdit)打开上面四个不同格式的文件(内容一样的),了解它们在编码形式上的区别(16进制显示):

在ANSI.txt文件其编码形式如下:

字母:A---0x41(65) a---0x61(97)  #一个字节

数字:123---0x31 0x32 0x33           #一个字节

汉字:中---0xD6 D0 国---0xB9 FA 严---0xD1 CF  #默认GB2321编码,两字节编码

在Unicode big endian.txt文件,其编码形式如下:

头标识:0xFE FF     #这叫大头方式存储,后面会介绍

字母:A---0x0041  a---0x0061  #两个字节表示(高字节补0,低字节同ANSI编码)

数字:123 ---0x0031  0x0032  0x0033

汉字:中---0x4E2D  国---0x56FD  严---4E25

在Unicode.txt文件中,其编码形式如下:

头标识:0xFF FE   #注意看,同上Unicode.txt文件中正相反,#这叫小头方式存储。

字母:A---0x4100  a---0x6100   #两个字节表示(高字节同ANSI编码,低字节补0)

数字:123 ---0x3100  0x3200  0x3300

汉字:中---0x2D4E  国---0xFD56  严---254E

在UTF-8.txt文件中,其编码形式如下:

   头标识:0xEF BB BF   #前三个字节"EF BBBF"表示这是UTF-8编码

字母:A---0x41   a---0x61 #同ASCII编码,单字节

数字:123---0x31  0x32  0x33 #同ASCII编码,单字节

汉字:中---0xE4 B8AD  国---0xE5 9BBD  严---E4 B8 A5#汉字三字节

小结:

1)ANSI是默认的编码方式。对于英文文件是ASCII编码,对于简体中文文件是GB2312编码(只针对Windows简体中文版,如果是繁体中文版会采用Big5码)。

2)Unicode编码指的是UCS-2编码方式,即直接用两个字节存入字符的Unicode码。这个选项用的little endian格式。

3)Unicode bigendian编码与上一个选项相对应。我在下一节会解释little endian和big endian的涵义。

4)UTF-8编码,也就是上一节谈到的编码方法。

3.2、little endian和big endian

回顾上面的例子,以汉字"中"为例,Unicode码是需要用两个字节存储,一个字节是2D,另一个字节是4E。存储的时候,4E在前,2D在后,就是Big endian方式(见上例中的Unicode big endian.txt文件0x4E2D);2D在前,4E在后,就是Little endian方式(见上例子中的Unicode.txt文件0x2D4E)。

计算机怎么知道某一个文件到底采用哪一种方式编码?即如何判断字符集。

四、如何判断采用不同字符集编码的文件

刚才我们谈了littleendian和big endian两种方式,本质就是编码字节序,即字节序分为Big Endian字节序和Little Endian字节序。对于前者而言,高位字节存在低地址,低字节存于高地址;后者相反。不同的处理器可能不一样。比如PC机的Inte处理器。

Unicode规范中定义,每一个文件的最前面分别加入一个表示编码字节顺序的字符,这个字符的名字叫做"零宽度非换行空格"(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且FF比FE大1。如果一个文本文件的头两个字节是0xFE FF,就表示该文件采用big endian方式;如果头两个字节是0xFF FE,就表示该文件采用little endian方式。

UTF-8格式文件,则文件的最前面加入的头标识“0xEF BB BF”

归纳成表格形式,更容易理解些,如下:

编码方式

文件头

UTF-8

  EF BB BF

UTF-16, little-endian

FF FE

UTF-16, big-endian

FE FF

UTF-32, little-endian

FF FE 00 00

UTF-32, big-endian

00 00 FE FF

GB2312

无文件头;但是,高字节和低字节的第1位都是1。

BIG5&GBK&GB18030

无文件头;但是,高字节的第1位为1。

ASCII

无文件头;但是,高字节的第1位为0。

 

 

 

 

 

 

 

从中不难看出,纯文本文件,通常不带文件头来标记文件类型,因为通过判断高字节的第1位从而知道是ASCII或者汉字编码。

到此,应该打开某些文件为何出现乱码有个清晰的认识了!

目的:为了理解文件在不同系统环境下(Linux、Windows)的编码转换打下基础。

五、UTF-8与GB系列对比

日常工作中UTF-8和GB系列编码使用频率很高,在此对比特点,加深大家的理解:

UTF-8和GB系列都兼容ASCII,与原ASCII对应的那些字符的编码不变,即普通英文字母、数字、控制码等字符,都还只是占一个字节!

它们都可以表示中文。GB系列是双字节编码,每个汉字占两字节,而UTF-8需要用三个字节来表示一个汉字字符。

GB系列是逐渐扩展出来的:GB2312-> GBK -> GB18030,可表示的字符越来越多。

UTF-8是UNICODE字符集编码方式之一,Windows环境下常采用文件头标识的方法来区分utf-16 utf-32编码的文件。一般linux下创建的utfF-8编码文件时默认不会加文件头标识。

六、附件:常用字符集清单

ASCII及其扩展字符集

作用:表语英语及西欧语言。

位数:ASCII是用7位表示的,能表示128个字符;其扩展使用8位表示,表示256个字符。

范围:ASCII从00到7F,扩展从00到FF。

ISO-8859-1字符集

作用:扩展ASCII,表示西欧、希腊语等。

位数:8位,

范围:从00到FF,兼容ASCII字符集。

GB2312字符集

作用:国家简体中文字符集,兼容ASCII

位数:使用2个字节表示,能表示7445个符号,包括6763个汉字,几乎覆盖所有高频率汉字。

范围:高字节从A1到F7, 低字节从A1到FE。将高字节和低字节分别加上0XA0即可得到编码。

GBK字符集

作用:它是GB2312的扩展,加入对繁体字的支持,兼容GB2312。

位数:使用2个字节表示,可表示21886个字符。

范围:高字节从81到FE,低字节从40到FE。

GB18030字符集

作用:它解决了中文、日文、朝鲜语等的编码,兼容GBK

位数:它采用变字节表示(1 ASCII,2,4字节)。可表示27484个文字。

范围:1字节从00到7F; 2字节高字节从81到FE,低字节从40到7E和80到FE;4字节第一三字节从81到FE,第二四字节从30到39。

BIG5字符集

作用:统一繁体字编码。

位数:使用2个字节表示,表示13053个汉字。

范围:高字节从A1到F9,低字节从40到7E,A1到FE。

UNICODE字符集

作用:为世界650种语言进行统一编码,兼容ISO-8859-1。

位数:UNICODE字符集有多个编码方式,分别是UTF-8,UTF-16和UTF-32。

UCS字符集

作用:国际标准 ISO 10646 定义了通用字符集(Universal Character Set)。它是与UNICODE同类的组织,UCS-2和UNICODE兼容。

位数:它有UCS-2和UCS-4两种格式,分别是2字节和4字节。

范围:目前,UCS-4只是在UCS-2前面加了0×0000。

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值