概述
做通信的时候,或者使用IO流读写数据的时候脑袋总是晕晕乎乎的。 与计算机打交道久了,不可避免会看到“鬼画符”式的乱码,让人云里雾里的,特别烦人。
本文将介绍为什么会出现乱码,以及字节、字符、字符集和编码之间的关系。
为什么会出现乱码?
乱码出现的原因主要有以下几个:
- 字符集不匹配:乱码最常见的原因是字符集不匹配。当文本使用的字符集与显示或处理该文本的程序所使用的字符集不一致时,就会出现乱码。例如,如果文本使用的是UTF-8字符集,但程序使用的是GBK字符集来解析该文本,就会导致乱码。
锟斤拷 产生的原因详见文末
- 编码错误:在文本传输或处理过程中,如果对文本的编码方式处理不当,也会导致乱码。例如,将一个使用UTF-8编码的文本错误地以GBK编码方式进行解析,就会出现乱码。譬如鸡同鸭讲。
类似下面这种编码和解码的“字符集”不同导致的混乱:
- 乒乓球拍卖完了(乒乓球拍这个商品卖完了/乒乓球在拍卖会上拍卖完了)
- 我要煮鸡蛋(我要煮过的鸡蛋/我要把鸡蛋煮了)
- A国对B国的进攻早有准备(A国已经准备好进攻B国了/A国已经做好准备应对B的进攻)
- 他叔父原来在上海一所不错的中学里当数学教师(他叔父之前是老师,现在可能不是/原来如此,他叔父是一个老师)
- 咬死了猎人的狗 (猎人被咬死了/狗被咬死了)
- 热爱人民的总理 (一位热爱人民的总理/我们都热爱人民的总理)
- 今年好晦气少不得打官司猪儿大似象老鼠死干净(今年好, 晦气少, 不得打官司, 猪儿大似象, 老鼠死干净 / 今年好晦气, 少不得打官司, 猪儿大似象老鼠, 死干净。)
- 单号给我核查(只要单号为奇数的单子/查单子的号码)
- 他爬过山没有?(有没有翻过这座山/有没有攀登山的经历)
- 他的故事讲不完(他知道的故事多/关于他的故事讲不完)
- 咬死了猎人的狗(狗咬死了猎人/猎人的狗被咬死了)
- 缺少字符映射:有些字符集可能不包含某些特殊字符或汉字,当这些字符在文本中出现时,如果没有正确的字符映射关系,就会导致乱码。
- 文本格式错误:如果文本本身存在格式错误,例如缺少必要的编码标识或转义字符,也会导致乱码。
- 程序处理错误:有些程序在处理文本时可能存在bug或错误,导致文本解析错误,从而产生乱码。
为了避免乱码问题,需要确保文本的字符集与程序所使用的字符集一致,并且在文本传输或处理过程中,要正确地处理编码方式。此外,还需要注意文本的格式正确性,以及程序的正确性。
为什么要编码?
字符必须编码后才能被计算机处理。 在计算机中所有的数据,在存储和运算时,都要用二进制数表示。
不知道大家有没有想过一个问题,那就是为什么要编码?我们能不能不编码? 要回答这个问题必须要回到计算机是如何表示我们人类能够理解的符号的,这些符号也就是我们人类使用的语言。 计算机只能处理二进制数据,需要将文字或符号转换为二进制形式进行处理。 由于人类的语言太多,因而表示这些语言的符号太多,无法用计算机中一个基本的存储单元—— Byte来表示,因而必须要经过拆分或一些“翻译/转换”工作,才能让计算机能理解。 目前计算机领域中广泛使用的是只有0和1的二进制语言,只能表示和存储二进制的数据,其它任何语言要能够在计算机中使用必须经过一次“翻译/转换”,把它“翻译/转换”成0和1的组合。这个“翻译/转换”的过程就是编码。编码可以实现文字或符号在计算机系统中传输和存储。
所以总的来说,编码的原因可以总结为:
- 计算机中存储信息的最小单元是一个字节/Byte即 8 个 bit,所以能表示的字符范围是 0~255 个
- 人类要表示的符号太多,无法用一个字节/Byte来完全表示
- 要解决这个矛盾计算机中需要一个新的数据结构 char,要实现从 char 到 Byte的相互转换则必须编码
字符是怎么编码的?
编码(Encoding):信息从一种形式或格式转换为另一种形式或格式的过程。 解码(Decoding):是编码的逆过程。就像查字典一样。
以ASCII字符集为例来聊聊字符是怎么编码的:
上图中的术语解释:
字符
字符(Character)指人类使用的文字或符号的总称,包括文字符号、图形符号、数学符号、字母、运算符号、标点符号和其他符号,以及一些功能性符号。可以由一个或多个字节表示。 一般来说我们称某个字符集里面的字符,叫xx字符,如ASCII字符集里面的ASCII字符,GB2312字符集里面的GB2312字符。
字符集
字符集(Character Set、Charset),一组字符的集合。每个字符在一个字符集中都有一个唯一的编码值(码点)。 字符集常常和一种具体的语言文字对应起来,该文字中的所有字符或者大部分常用字符就构成了该文字的字符集,比如英文字符集。 一组有共同特征的字符也可以组成字符集,比如繁体汉字字符集、日文汉字字符集。 字符集的子集也是字符集。 字符集通常用来框定一个范围。包含这些字符就已经够了,可以满足日常使用了。
常见字符集:ASCII字符集、GB2312字符集、BIG5字符集、GBK字符集、 GB18030字符集、Unicode字符集等。
码位
在字符编码术语中,又叫码点(Code Point)、编码位置,是一个字符集编码表为每一个字符分配一个唯一的数字ID。 例如,ASCII码包含128个码位,范围是016进制到7F16进制,扩展ASCII码包含256个码位,范围是016进制到FF16进制,而Unicode包含1,114,112个码位,范围是016进制到10FFFF16进制。
相同的码点在不同的字符集中映射的字符不一定是相同的。 这也是乱码的直接原因。
字符编码
字符编码(Character Encoding)规定每个“字符”分别用一个字节还是多个字节存储,用哪些字节来存储,是把字符集中的字符按一定方式转换/翻译为一个指定集合中的某一对象的规则。 外部程序通过这种编码就可以从字符集文件中调用指定的字符。我们常见的计算机字体文件就使用了字符集编码,通过输入法输入文字或者浏览网页时都会通过指定的字符集编码从字体文件中调用字符。 譬如将字符编码为由0和1两个数字所组成的位串模式、由0~9十个数字所组成的自然数序列或电脉冲等,亦即在字符集与指定集合两者之间建立一个对应关系(即映射关系)的过程。这是信息处理的一项基础技术。常见的例子包括将拉丁字母表编码成摩斯电码和ASCII码。
字符集会影响字符编码。譬如汉字有8万多个,肯定无法使用ASCII的单字节编码,因为ASCII能表达的字符太少,放不下那么多汉字。
各个国家和地区在制定编码标准的时候,“字符集”和“编码”一般都是同时制定的。
比特(bit):也可称为“位”,是计算机中最小的存储单位,是 binary digit(二进制数位)的缩写,指二进制中的一位。
字节(Octet/Byte):计算机中信息计量的一种单位,一个位就代表“0”或“1”,每8个位(bit)组成一个字节(Octet/Byte)。
GBK一个中文字符编码时会使用两个字节的形式存储 GBK规定: 汉字的第一个字节的第一位必须是 1
为什么要有字符集?
不同国家或民族的文字或符号要在计算机中存储和展示,就需要先让计算机能理解。 人类要表示的符号太多,如果要让计算机能理解所有的符号,工作量太多,且不是必要的。当初设计的时候,谁知道计算机会这么普及呢? 譬如你要看一本英文的书,要不要把日语一块学了,然后再这本英文书?这种在项目管理的角度看这种做法是“镀金”,在老板看来就叫做“脱裤子放屁”,因为只用学会英文就已经够了。
科研人员在交付系统时只需要确保计算机能理解和表达特定范围内的字符就可以了。这个范围内字符的集合就是字符集。
为什么会有这么多字符集?搞一个不就行了吗?
制定字符集是有成本的,出现这么多字符集是有历史原因的。 目前的万国码(Unicode编码标准)已经包含了世界上所有的文字、符号、数字、标点等字符。
我们从不同字符集诞生的历程来看看为什么会有这么多不同的字符集:
如果计算机一直只有美国人使用的话,没有问题。
但是后来当一些欧洲国家也开始使用计算机的时候,比如法国、德国。他们会发现,我们国家里的字符不在你这128个字符中。
于是法德两国在原有ASCII码的基础上进行了扩展。把原来的第一位0变成了1,也就是从128个字符扩展到了256个字符,即又新增了128个。 在ASCII字符集基础上包含了新增128个字符的字符集就叫做扩展ASCII字符集或ISO-8859-1字符集或Latin-1。
这个办法,解决了一些欧美国家的字符编码问题。
此时,在中国要想使用计算机,是不是也要对用到的字符进行编码。
这样看来,是不是每个国家都可以设计一套属于自己国家的编码:
随着计算机技术发展迅速,各国之间的信息交流也越来越频繁,但是不同国家、不同语言之间的文字编码标准却各不相同,这给信息交流带来了很大的困难。 为了解决这个问题,Unicode联盟成立了,开始制定Unicode编码标准,并不断完善和更新。目前,Unicode编码标准已经成为了世界上最广泛使用的文字编码标准之一,它可以支持超过13万种字符,包括世界上所有的主要文字系统。
万国码(Unicode编码标准)是一种用于表示文字的编码标准,它可以将世界上所有的文字、符号、数字、标点等字符都编码成数字,以便计算机能够识别和处理。
Unicode编码标准是一种国际标准,它由国际组织Unicode联盟制定,目的是为了解决不同国家、不同语言之间的文字编码问题。
中文编码的发展历程
ASCII
ASCII(American Standard Code for Information Interchange)美国标准信息交换码,是第一个标准字符集和编码。ASCII字符集迄今为止共收录了 128 个字符,包含了基本的拉丁字母(英文字母)、阿拉伯数字(也就是 1234567890)、标点符号(,.!等)、特殊符号(@#$%^&等)以及一些具有控制功能的字符(往往不会显示出来)。 ASCII首次发布于1963年,并于1967年成为美国国家标准,后经数次修订,1986年定型最终版本使用至今。后被ISO(International Standardization Organization)国际标准化组织定为国际标准ISO/IEC 646。ASCII码对照表是世界最通用的信息交换标准。
ASCII使用用一个字节(8 bits)进行编码,属于单字节编码。因此最多只能表示256个字符。基础的ASCII使用7bits编码,最高位位为0,或者用于奇偶校验。 ASCII编码适用于所有的拉丁字母。
ASCII既可以指ASCII字符集,也可以指ASCII编码。
后来,由于各国语言的加入,ASCII已经不能满足信息交流的需要,因此,为了能够表示其它国家的文字,各国在ASCII的基础上制定了自己的字符集,这些从ANSI标准派生的字符集被习惯的统称为ANSI字符集,它们正式的名称应该是MBCS(Multi-Byte Chactacter System,即多字节字符系统)。这些派生字符集的特点是以ASCII 127 bits为基础,兼容ASCII。
GB2312
GB2312全称《信息交换用汉字编码字符集——基本集》,是中国最早的中文字符集,属于双字节字符集 (DBCS)。也是中华人民共和国国家汉字信息交换用编码,使用2个字节表示中文字符,属于双字节编码。于1980年由国家标准总局发布。
GBK既可以指GBK字符集,也可以指GBK编码。
GB2312字符集只收录了6763个汉字,因此许多以前很少使用的生僻字没有收录,现在这些字也许变得流行了,例如:朱镕 基的“镕”字,未收入GB2312-80,现在大陆的报业出刊只得使用(金 容)、(金容)、(左金右容)等来表示,形式不一而同,这使得表示、存储、输 入、处理都非常不方便。
Big5
Big5是用于繁体中文的字符集,属于双字节字符集 (DBCS)。采用2个字节表示中文字符,属于双字节编码。于1984年发布。
ISO8859-1
ISO8859-1 字符集,也就是 Latin-1,是西欧常用字符,包括德法两国的字母,是国际标准化组织 (ISO) 认可的 8 位字符集。于1987年发布。 ISO-8859-1 仍然是单字节编码,它总共能表示 256 个字符。向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致。
ISO-8859-1字符集应用最广泛。 因为ISO-8859-1编码范围使用了单字节内的所有空间,在支持ISO-8859-1的系统中传输和存储其他任何编码的字节流都不会被抛弃。这是一个很重要的特性。 换言之,把其他任何编码的字节流当作ISO-8859-1编码看待都没有问题。 ISO8859-1是 HTML 4.01 中的默认字符。
Unicode
Unicode是一种全球字符集,支持几乎所有的字符,它将世界各种语言的每个字符定义一个唯一的编码,以满足跨语言、跨平台的文本信息转换。于1991年发布。Unicode是由国际组织设计,是一个可以容纳全世界所有语言文字的字符集。Unicode的学名是“Universal Multiple-Octet Coded Character Set”,简称为UCS。UCS可以看作是“Unicode Character Set”的缩写。
Unicode指Unicode字符集。 Unicode的编码有多种实现方式,譬如UTF-8编码、UTF-16编码、UTF-32编码等
GBK
GBK(Chinese Internal Code Specification)是GB2312的扩展,属于双字节字符集 (DBCS)。支持更多的中文字符。于1995年发布。该编码标准兼容GB2312,即同一个字符在这些方案中总是有相同的编码,共收录汉字21003个、符号883个,并提供1894个造字码位,简、繁体字融于一库。包括了GB2312中的全部汉字、非汉字符号、BIG5中的全部汉字、其它汉字、部首、符号,共计984个。
GBK既可以指GBK字符集,也可以指GBK编码。
GB18030
GB18030字符集是最新的汉字编码字符集国家标准, 向下兼容 GBK 和 GB2312 标准,即同一个字符在这些方案中总是有相同的编码,是GBK的升级版,支持更多的中文字符和国际字符。于2000年发布。采用单字节、双字节和四字节三种方式对字符编码。
GB18030既可以指GB18030字符集,也可以指GB18030编码。
最新的中文字符集是Unicode的一部分,可以使用UTF-8编码进行表示。
小结
本文以乱码为切入口,梳理了微机原理中字符的编码、字符集相关知识。 我们了解到编码是将人类文明系统中的文字或符号映射到计算机的二进制系统的过程,只有经过编码能够让计算机能够识别和处理,将人类世界的符号系统与计算机系统打通,而字符集定义了字符和编码值之间的映射关系。了解这些概念可以帮助我们更好地理解和解决乱码问题。
加油站
1、锟斤拷——锟(0xEFBF),斤(0xBDEF),拷(0xBFBD)
这个问题是在早期的Unicode版本中存在的,后续的Unicode版本对这个问题进行了修复。目前的Unicode版本已经没有这个问题,可以正确地表示所有字符。
之前在搜索引擎和网站上经常看到这三个有点不正经的字符。 这个乱码产生的原因是GBK字符集和Unicode字符集之间的转换出了问题。Unicode和老编码体系的转化过程中,肯定有一些字,用Unicode是没法表示的,Unicode官方用了一个占位符来表示这些文字,这就是:U FFFD REPLACEMENT CHARACTER。那么U FFFD的UTF-8编码出来,恰好是 'xefxbfxbd'。如果这个'xefxbfxbd',重复多次,例如 'xefxbfxbdxefxbfxbd',然后按照GBK/CP936/GB2312/GB18030的的编码规则,一个汉字2个字节,最终的结果就是:锟斤拷——锟(0xEFBF),斤(0xBDEF),拷(0xBFBD)。
后来的Unicode版本通过不断更新和扩展字符集来修复这个问题。 Unicode不断地添加新的字符,以覆盖各种语言和符号系统中的所有字符。通过这种方式,Unicode能够正确地表示所有的字符,包括之前无法表示的字符。
Unicode的修复过程主要包括以下几个方面:
- 添加新的字符:Unicode不断地添加新的字符,以覆盖各种语言和符号系统中的所有字符。这样,原本无法表示的字符就可以通过新添加的字符来表示了。
- 扩展编码空间:Unicode通过扩展编码空间来容纳更多的字符。最初的Unicode版本使用16位编码,可以表示65536个字符。后来,Unicode扩展到了21位编码,可以表示超过100万个字符。
- 规范化处理:Unicode还引入了规范化处理的概念,用于处理字符的等价性和兼容性。规范化处理可以将不同的字符序列转换为统一的规范形式,从而避免了字符转换过程中的乱码问题。
通过以上的修复措施,现在的Unicode版本已经能够正确地表示所有的字符,解决了之前存在的乱码问题。
2、 Octet 和 Byte
在读RFC或网络设备文档时,经常见到Octet这量词来指代8位(bit),为啥不用Byte呢?有啥区别?
Octet 总是表达8个bit (就像他名字所说的),当我们在谈论网络上的问题时,更喜欢使用这个词而不是Byte。
Byte 通常情况下也表示8个bit,但是准确的说,Byte表示的是CPU可以独立的寻址的最小内存单位(不过通过移位和逻辑运算,CPU也可以寻址到某一个单独的bit)。曾几何时,有些电脑的寻址单位不是8Bit的。而现在,大多数情况下byte可以和octet等价但是如果你想要强调准确的8bit时,你就应该使用octet而不是byte。
另外,还有一个量词 nibble用来形容一个字节的一半 (通常是4bit)不过这个词基本上没人用就是了。
#从外面打开是食物,从里面打开的是生命#
引用
https://www.cnblogs.com/softidea/p/4252698.html
中文有哪些容易让人产生歧义的词语或说法?https://www.zhihu.com/question/59099277
中文有哪些容易让人产生歧义的词语或说法?https://www.zhihu.com/question/59099277
字符编码的前世今生 https://tgideas.qq.com/webplat/info/news_version3/804/7104/7106/m5723/201307/218730.shtml
HTML ISO-8859-1 参考手册 https://www.w3school.com.cn/charsets/ref_html_8859.asp
字符编码笔记:ASCII,ANSI, GBK、ISO-8859-1, Unicode 、 UTF-8 https://blog.csdn.net/qq_30436011/article/details/128041513
字符、字符集、字符编码的基础知识科普 https://zhuanlan.zhihu.com/p/260192496
汉字字符集编码查询 https://www.qqxiuzi.cn/bianma/zifuji.php
http://www.differencebetween.com/difference-between-octet-and-vs-byte/
GBK 编码表 https://www.toolhelper.cn/Encoding/GBK
非常详细的字符编码讲解,ASCII、GB2312、GBK、Unicode、UTF-8等知识点都有 https://www.bilibili.com/video/BV1gZ4y1x7p7/
一听就懂字符集、ASCII、GBK、UTF-8、Unicode、乱码、字符编码、解码问题的讲解 https://www.bilibili.com/video/BV1xD4y1y7yc/
13-ASCII码-动画 https://www.bilibili.com/video/BV123411u7JU/