在平常Ulink活动跟进过程当中,有时活动页面打开的时候会遇到乱码的状况(以下图所示),因而就想乱码究竟是怎么产生的,遇到乱码的状况应该怎么去解决,带着这些问题,我去查阅了相关的资料,在这里整理成文章分享给你们,但愿对你们有所帮助。html
咱们都知道,计算机是只认识0和1的二进制数的,因此无论是字母,汉字,或者符号,都是以某种编码方式转换成二进制数据存放在计算机中,须要显示的时候,就用相同的编码方式把二进制数据解码出来就能够了。那么这就很好理解乱码的产生了,若是咱们用A编码方式将字符进行编码,而后用B编码方式来解码,解码出来的就确定是乱码。咱们用一个比较风趣的故事能够很形象的说明这个问题:有一天你带着女友去见一个老外朋友,见面时老外很热情的赞扬道 your girl friend is so beautiful,而后你本着中华名族谦逊有礼的优良传统,回答“where”,这时你的老外朋友应该就是这个表情了…编程
这个故事中,你想表达的“哪里”这个内容用博大精深中文编码后是谦虚的意思,可是老外用他本身的思惟去解码后就是哪里的本意,固然会不明白什么意思了。浏览器
要搞清楚乱码的问题,我以为应该从这些很容易混淆的基本概念提及。譬如什么是字符,字符编号,字符集等。微信
字符是具备语义值的最小文本单位,包括字母、数字、运算符号、标点符号和其余符号,以及一些功能性符号,字符也是数据结构中最小的数据存取单位,有的人可能会有一些误解,认为英文字母或者特殊符号占一个字节,而中文汉字是占2个字节,其实这种想法不许确的,字符在计算机中所占的字节数和它的字符编码有关,在GB2312字符编码中,一个汉字是占2个字节,可是在UTF-8字符编码中,是占3个字节,因此在不知道哪一种字符编码的时候说字符占多少个字节的说法是不够严谨的。markdown
字符编号是用来表示一个字符的号码,相似咱们的身份证号码,具备不可重复性,同一个字符在不一样的字符集中,他的字符编号也是不同的,这个也很好理解,就像你在中国的护照号码和你移民到了美国的护照号,你仍是你,可是护照号已经不同了。网络
字符集是多个字符的集合,字符集种类较多,每一个字符集包含的字符个数不一样,常见字符集有:ASCII字符集、GB2312字符集、Unicode字符集等。不一样的字符集包含的字符个数也不相同,好比ASCII字符集收录了128个字符,而GB2312收录了7445个字符。日常你们所说的字符集准确的讲应该叫编号字符集,它是指用字符编号来标示某个字符集中每个字符后生成的字符集合,听着有点绕,为啥要给字符加个编号呢?主要仍是能方便定位找到某个指定的字符,这个和每一个人生出后都会起一个姓名同样。数据结构
字符编码也称字集码,是把字符集中的字符,编码后生成指定集合中某一对象(例如:比特模式、天然数序列、8位组或者电脉冲),以便文本在计算机中存储和经过通讯网络的传递。这是百度百科对它的专业定义,本人的理解是字符集中的字符标号和实际存储的二进制数据之间的映射关系,譬如在ASCII字符编码中字母A的编号是65,因此它存入计算机时的值就是’01000001’。编程语言
有些人比较容易混淆字符集和字符编码这两个概念,通俗的讲,字符集是用字符编号标示后的字符集合,而字符编码是指字符用什么方式转化成二进制数据的规则。编码
编码是信息从一种形式或格式转换为另外一种形式的过程,也称为计算机编程语言的代码(简称编码)。用预先规定的方法将文字、数字或其它对象编成数码,或将信息、数据转换成规定的电脉冲信号。通俗的理解就是将字符按必定的规则转换成二进制数据。spa
与编码相对,解码是一种用特定方法,把数码还原成它所表明的内容或将电脉冲信号、光信号、无线电波等转换成它所表明的信息、数据等的过程。解码是将接受到的符号或代码还原为信息的过程,与编码过程相对应。
文章开头也说到数据都是以二进制的形式存储在计算机中,那哪些二进制数据表示哪些字符,每一个人都有一套本身的规则,这样就不能互相通讯,为了解决这个问题,美国相关标准化组织就出台了ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)编码,统一了二进制数据对应字符的规则,ASCII字符集一共包含128个字符(0-127)。其中有33个控制字符(不可显示字符),52个英文字母(包括大小写),0~9十个阿拉伯数字,其他为一些特殊符号。它使用一个字节的后7位来表示某个字符,对应的二进制编码是0000 0000 ~ 0111 1111,其中最高位通常为0,但有时也被用做一些通信系统的奇偶校验位,就像上面举例的大写字母A的二进制编码就是’01000001’。
随着计算机技术的慢慢发展,人们发现ASCII字符集的128个字符已经不能知足他们的需求了,因而ISO8859-1字符集就诞生了,它共有256个字符,向下兼容ASCII,编码范围是0x00-0xFF,其中0x00-0x7F之间彻底和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。它属于西欧语系中的一个字符集,支持希腊语、丹麦语、阿拉伯语等,和ASCII字符集同样,ISO8859-1字符集也是使用默认的字符编码来把字符编号转换成二进制数据存储在计算机中。
GB2312字符集全称为《信息交换用汉字编码字符集·基本集》,由原中国国家标准总局发布,1981年5月1日实施。计算机到中国的时候,已经没有空余的字节空间存放中文了,并且中文有那么多汉字,怎么也放不下,因此只好另外再新建一种字符集,因而GB2312字符集就产生了,它使用两个字节来表示一个汉字,前面的一个字节(高字节)使用0xA1-0xF7这个区间,后面一个字节(低字节)使用0xA1-0xFE这个区间,这样咱们就能够组合出大约存放7000多个字符的空间了。其中包括6763个汉字(一级汉字3755个,二级汉字3008个),已经能够覆盖99.75%的汉字使用频率,基本知足了汉字的计算机处理须要。GB2312字符集还把符号、罗马希腊字母、日文假名都编进去了,就连ASCII 里原本就有的数字、标点、字母都从新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号如下的那些就叫"半角"字符。全角和半角经过高字节的最高位是1仍是0来判断,1表示全角,按照两个字节来解码,0表示半角,按照1个字节来解码。那这么多字符是怎么排列的呢?GB2312中对所收汉字进行了“分区”处理,每一个区含有94个汉字/符号。这种表示方式也称为区位码。各区包含的字符以下:01-09区为特殊符号;16-55区为一级汉字,按拼音排序;56-87区为二级汉字,按部首/笔画排序;10-15区及88-94区则未有编码。下面举一个具体例子说明是怎么存放的。GB2312编码是将区码和位码分别加上160(十进制)的偏移量以后(由于要兼容ASCII码),再转化成二进制存放到计算机中的,譬如“有”字的区位码是5148,区码51和位码48分别加上160之后就是211和208,因此它在计算机中的存储值就是’1101001111010000’。
计算机到了各个国家后,每一个国家都各自搞了一套能兼容ASCII字符集但又不能互相兼容的编码方案,这样就会致使一个国家的二进制编码到了另一个国家不能被正常解码的问题,对数据传输带来很大的障碍(就像古代秦国统一其余六国后也面临着这个问题),这时候就迫切须要有一种可以容纳世界上全部语言和符号的字符集,因而Unicode字符集就这样万众期待的诞生了。在Unicode字符集中,每个字符都有一个独一无二的编码,这也正好印证了它的名字,Unique(惟一)和code(编码)二者的结合就成了Unicode。它一共有17个平面(0到16),每一个平面里又分了不少页,每一页里又有不少行,而咱们的字符就按照各自的字符编号,静静的存放在每一行里。目前17个平面中目前只用到0号、1号、2号、3号和14号平面,其中咱们的汉字在0号、2号和3号平面,其它文字在0号、1号和14号平面;譬如“当”字就存放在第0号平面,第6页,第96行,他的编号就是5F53。
Unicode的产生标志着字符编码领域进入了一个全新时代,不只是由于它存储了世界上全部的字符,并且它还把字符集和字符编码的定义严格的区分开来,在Unicode诞生以前,全部的字符集都是和字符编码捆绑在一块儿的,这种方式的缺点在与字符和字节流之间的耦合度过高,致使它的可扩展性不强。而Unicode考虑到了这点,能够有不一样的字符编码(UTF-八、UTF-16等),也就是说,决定最终字节流的是字符编码,譬如对字母“A”进行UTF-8编码,获得的字节流是0x41,用UTF-16进行编码,获得的倒是0x00 0x41。下图是GB2312和Unicode的对比,很好的说明了这个问题。
在Unicode众多编码方式中,咱们最多见到的就是UTF-8,下面就详细说一说这个编码方式。就像上面所说,字符编码实际上是从字符编号到实际存储二进制字节流的映射,下面这张表能够分析它是怎么实现这个映射关系的。表中x表明序号部分,把各个字节中全部x拼接在一块儿就组成了在Unicode字符集中的字符编号,其中第一个字节能够是0,110,1110开头,这样以此类推,可是从第二个字节开始,后面每一个字节都只能以10开头,不一样的数字开头分别表示的意思以下:
由于篇幅关系,这里举一个最多见的HTML页面的乱码场景,咱们项目指定使用UTF-8编码,可是在html文件中,咱们使用GBK编码,用浏览器运行后就出现了乱码。
下面咱们来具体分析一下是怎么产生这些乱码的,文章一开始也说了,乱码的产生其实就是编码和解码使用的方式不一致产生的,UTF-8和GBK都很好的兼容了ASCII字符集,因此它们对单字节的英文字母或者数字解码时不会有什么差异,可是对中文汉字就不同了,对UTF-8而言,一个汉字占3个字节,因此对原本要显示的这段文本编码后产生的二进制字节流以下图左边部分所示,而对GBK而言,一个汉字是占两个字节的,第一个字节的十进制值大于128,则认为这个是表示中文汉字,会将它和后面一个字节连起来一块儿解码,因此这段文本用GBK解析后从新分组排列就变成了下图右边部分所示,除了第五组和第十组是一个字节,其余每组都是两个字节(由于它们的第一个字节的十进制值都大于128),其中第五组是由于第一个字节小于128,第十组是由于后面没有字节了。咱们经过每组的二进制值在GBK字符集中找到它对应的位置,查到的具体字符和浏览器显示的是一致的。
若是想让网页显示正常的文本其实很简单,只要将html中的编码改为UTF-8就能够了。
从第一个字符集的诞生到后面不断有新的字符集产生,其实都是由于随着计算机的发展,原有的字符集知足不了当前的需求的缘由,而后才会有不一样的编码方式来编码和解码,最终产生的乱码,从上面的例子中也不难看出,其实相似咱们日常看一句句子,用不一样的断句方式,可能会产生彻底不同的句意,但愿经过这篇文章,能够帮你们把字符集,字符编码理清楚,遇到乱码的时候,只要能分析好每次编码和解码使用的方式是否一致,这样乱码的问题天然会迎刃而解了。
更多精彩内容,尽请关注腾讯VTeam技术团队微信公众号和视频号
原做者:裘维清
未经赞成,禁止转载!