乱码是怎么回事?

一、前言
在程序员的世界里,乱码是个比较常态的现象。为究其原因,下面我们沿用著名的”费曼学习法“追根溯源剖析常见乱码原因与解决办法,以加深理解。
费曼学习法:
① 确定学习目标。
② 理解所学内容。
③ 把所学内容将给他人。
④ 对讲不清楚的内容,反复学习直到彻底掌握。
本文目标:
1、 乱码示例
2、常见乱码现象
3、了解计算机的编码方式
4、乱码的根本原因
5、解决乱码的基本方法
二、乱码示例
乱码是在一定条件下呈现了与实际内容不符的现象,例如我有一个文件正常情况是这样的,某一天打开时又成了这样,这种现象就是所谓的乱码。
why is this,what happened?
2、乱码现象
一个只懂英语的人阅读一本用希腊语翻译后的英语小说,这本小说对他来说就是一堆乱码。这里的一种语言就代表一种编码,语言不通则内容不懂。我们经常在以下场景看到乱码:
①文本阅读乱码:一般是读写编码不一致。
②网络传输乱码:http头中设置正确字符集。
③数据库操作乱码:程序与数据库使用字符集不一致。
④控制台或日志文件乱码:编辑器或者程序内部解析错误。
⑤数据处理乱码:输入输出流的编码格式不一致。
以上这些现象都编码问题所导致;想要究其根本,必须要了解计算机中的编码知识。
3、计算机中的编码方式
3.1、计量单位
在理解计算机编码方式之前,首先要弄清楚几个概念(位、字节、字符、字符集)。
bit(位)
位是计算机中度量、存储信息的最小单位,只有0或1两种二进制状态,每个0或1表示1个bit。
Byte(字节)
字节是计算机中数据处理的基本单位,用大写字母B表示。1Byte=8bit,在mac或Linux平台上文件目录通常用字节来表示文件大小。
字符(character)
字符是计算机中用来表示各种信息的一组符号,便于人类识别。这些符号可以是数字、字母、汉字、标点符号、控制符、空格等,如(中,a,b,1,+,”,space ,/,cr,esc)。
字符集(character set)
字符集是多种字符的集合,集合中字符越多,所能表示的信息量也就越大。
3.2、常见编码方式
①ASCII是最知名编码标准,出自漂亮国。用7位二进制(00000000~01111111)编码,十进制表示为0~127,每个编码代表一个基本符号。因此,最多可表示128个基本符号,其中33个控制字符(范围0~31和127),95个可显示字符(范围是32-126「0x20-0x7E」)。详情见百度ASCII码表
②Unicode是ISO组织定义的一套编码方案,又称为万国码。它不是一个新编码规则而是一套统一的字符集,可将其理解为一本世界字典。ISO规定每一个字符都使用16位二进制来表示所有字符。对ASCII表里的字符保持编码不变,只是将长度扩展到16位,其他字符则重新编码。这样一来虽然统一了编码,同时也带来了新问题:存储ASCII中的字符本来需要1个字节,现在需要2个字节。造成了网络传输浪费带宽,存储数据浪费空间。
③UTF-8是一种被广泛使用的互联网信息编码规则,采用可变长编码方式;能表示unicode标准中的任意字符同时兼容ASCII编码规则。UTF-8由4种编码方式(utf8-1/utf8-2/utf8-3/utf8-4),分别表示所占二进制位数8,16,24,32。
④GBK是国人定义的双字节编码,最早叫做GB2312。规则是:小于127位的字符与ASCII一样,大于127的两个字符连在一起表示一个汉字。第一个字节的高位(从0xA1-到0xF7),第二个字节低位(从0xA1-到0xFE),这样组合可以表示7000多个简体汉字。为了支持更多的汉字,之后进行扩展取消了第二个字节必须大于127的限制,最终可支持约2万多个汉字和符号。
4、乱码的原因
通过前面的内容我们知道,计算机中所有数据最终都是以一个或多个字节的形式来表达。同一个字符不同编码方式所占字节数也不一样。接下来我们来看相同内容在不同编码方式下的编码、解码过程。
4.1、编码
编码就是将可显示字符转换成二进制的形式,例如:大写字母”A”的ASCII十六进制是”0x40″,二进制是”01000001″;中文”德”在utf-8中的十六进制”0xe5beb7,转换成二进制是”1110 0101 1011 1110 1011 0111″。根据使用的普遍性,我们重点来看UTF-8的编码规则。

utf8编码长度                                 表示范围
#1-byte:0xxxxxxx  			    : U+0000 -> U+007F
#2-byte:110xxxxx 10xxxxxx		    : U+0080 -> U+07FF
#3-byte:1110xxxx 10xxxxxx 10xxxxxx	    : U+0800 -> U+FFFF
#4-byte:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx : U+10000 -> U+10FFFF

参照utf-8的规则,我们将中文”德“字的utf-8二进制形式转换成unicode中的二进制形式,详见下图:

根据图中所示可知“德”字在unicode中的码位是”0x5fb7″。至此,我们能理解计算机要正确显示和转换字符,必然要清楚两个要素:字符对应的二进制字节数组和与之对应的编码(utf8/gbk等)。字符在计算机的输入输出过程中,时常存在编码不一致的场景。例如服务器端是utf-8,客户机是GBK,这就需编码和解码两个过程。编码是计算机根据目标编码所表示的字符,将其转换成二进制字节。解码是根据二进制字节得到编码,从而确认是什么字符。
4.2、原因
通过上述铺垫,可逻辑推出导致乱码的根本原因有二:
1)识别编码方式错误
代码演示:

2)编码信息丢失
所谓丢失是指字节数组在编码时丢失了一定的位数。说白了就是把一个长度为3字节的字符强行用1字节编码,导致二进制位丢失,即便是解码时的编码方式正确也一样显示乱码,不再用代码展示,有兴趣的按照上图代码可验证。
5、解决乱码的基本方法
其实在4.2小节的两个原因中大家可以找到正确的应对方法。



发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注