要理解乱码问题,首先需要理解几个概念:字符集、编码、编码规则、乱码

1. 字符集:

字符(Character)是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。字符集(Character set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集名称:ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集、Unicode字符集等。其实字符集简单了来说,就是一张表格,是 id 和字符的对应表。

2. 各种编码:

一种编码格式必须选定一个字符集。比如 UTF-8和 UTF-16 / UTF-32 选用 Unicode 字符集,GB2312选用GB2312字符集字符集。

3. 不同字符集的编码、解码规则:

(1)UTF-8的编码规则:

UTF-8是一种变长字节编码方式。对于某一个字符的UTF-8编码,如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的位数,其余各字节均以10开头。UTF-8最多可用到6个字节。 
如表: 
1字节 0xxxxxxx 
2字节 110xxxxx 10xxxxxx 
3字节 1110xxxx 10xxxxxx 10xxxxxx 
4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
因此UTF-8中可以用来表示字符编码的实际位数最多有31位,即上表中x所表示的位。除去那些控制位(每字节开头的10等),这些x表示的位与UNICODE编码是一一对应的,位高低顺序也相同。 
实际将UNICODE转换为UTF-8编码时应先去除高位0,然后根据所剩编码的位数决定所需最小的UTF-8编码位数。 
因此那些基本ASCII字符集中的字符(UNICODE兼容ASCII)只需要一个字节的UTF-8编码(7个二进制位)便可以表示。

(2)UTF-16的编码规则:

UTF-16是Unicode字符集的一种转换方式,即把Unicode的码位转换为16比特长的码元串行,以用于数据存储或传递。

2.2.1 从U+D800到U+DFFF的码位(代理区)

因为Unicode字符集的编码值范围为0-0x10FFFF,而大于等于0x10000的辅助平面区的编码值无法用2个字节来表示,所以Unicode标准规定:基本多语言平面内,U+D800..U+DFFF的值不对应于任何字符,为代理区。因此,UTF-16利用保留下来的0xD800-0xDFFF区段的码位来对辅助平面的字符的码位进行编码。

但是在使用UCS-2的时代,U+D800..U+DFFF内的值被占用,用于某些字符的映射。但只要不构成代理对,许多UTF-16编码解码还是能把这些不符合Unicode标准的字符映射正确的辨识、转换成合规的码元. 按照Unicode标准,这种码元串行本来应算作编码错误.

2.2.2 从U+0000至U+D7FF以及从U+E000至U+FFFF的码位

第一个Unicode平面(BMP),码位从U+0000至U+FFFF(除去代理区),包含了最常用的字符。UTF-16与UCS-2编码在这个范围内的码位为单个16比特长的码元,数值等价于对应的码位。BMP中的这些码位是仅有的码位可以在UCS-2被表示。

2.2.3 从U+10000到U+10FFFF的码位

辅助平面(Supplementary Planes)中的码位,大于等于0x10000,在UTF-16中被编码为一对16比特长的码元(即32bit,4Bytes),称作 code units called a 代理对(surrogate pair),具体方法是:

Ø 码位减去0x10000, 得到的值的范围为20比特长的0..0xFFFFF(因为Unicode的最大码位是0x10ffff,减去0x10000后,得到的最大值是0xfffff,所以肯定可以用20个二进制位表示),写成二进制形式:yyyy yyyy yyxx xxxx xxxx。

Ø 高位的10比特的值(值的范围为0..0x3FF)被加上0xD800得到第一个码元或称作高位代理(high surrogate), 值的范围是0xD800..0xDBFF。由于高位代理比低位代理的值要小,所以为了避免混淆使用,Unicode标准现在称高位代理为前导代理(lead surrogates)。

Ø 低位的10比特的值(值的范围也是0..0x3FF)被加上0xDC00得到第二个码元或称作低位代理(low surrogate), 现在值的范围是0xDC00..0xDFFF。 由于低位代理比高位代理的值要大,所以为了避免混淆使用,Unicode标准现在称低位代理为后尾代理(trail surrogates)。

Ø 最终的UTF-16(4字节)的编码(二进制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。

按照上述规则,Unicode编码0x10000-0x10FFFF的UTF-16编码有两个WORD,第一个WORD的高6位是110110,第二个WORD的高6位是110111。可见,第一个WORD的取值范围(二进制)是11011000 00000000到11011011 11111111,即0xD800-0xDBFF。第二个WORD的取值范围(二进制)是11011100 00000000到11011111 11111111,即0xDC00-0xDFFF。上面所说的从U+D800到U+DFFF的码位(代理区),就是为了将一个WORD(2字节)的UTF-16编码与两个WORD的UTF-16编码区分开来。

由于高位代理、低位代理、BMP中的有效字符的码位,三者互不重叠,搜索是简单的: 一个字符编码的一部分不可能与另一个字符编码的不同部分相重叠。这意味着UTF-16是自同步(self-synchronizing):可以通过仅检查一个码元就可以判定给定字符的下一个字符的起始码元。 UTF-8也有类似优点,但许多早期的编码模式就不是这样,必须从头开始分析文本才能确定不同字符的码元的边界。

由于最常有的字符都在基本多文种平面中,许多软件的处理代理对的部分往往得不到充分的测试。这导致了一些长期的bug与潜在安全漏洞,甚至在广为流行得到良好评价的应用软件。

(3)UTF-32的编码规则:

UTF-32编码以32位无符号整数为单位。Unicode的UTF-32编码就是其对应的 32位无符号整数。
字节序
根据字节序的不同,UTF-16可以被实现为UTF-16LE或UTF-16BE,UTF- 32可以被实现为UTF-32LE或UTF-32BE。例如:
Unicode编码 ║ UTF-16LE ║ UTF-16BE ║ UTF32-LE ║  UTF32-BE 
0x006C49 ║ 49 6C ║ 6C 49 ║ 49 6C 00 00 ║ 00 00 6C 49 
0x020C30 ║ 43 D8 30 DC ║ D8 43 DC 30 ║ 30 0C 02 00 ║ 00 02 0C 30 
那么,怎么判断字节流的字节序呢?Unicode标准建议用BOM(Byte Order Mark)来区分字节序,即在传输字节流前,先传输被作为BOM的字符"零宽无中断空格"。这个字符的编码是FEFF,而反过来的FFFE(UTF- 16)和FFFE0000(UTF-32)在Unicode中都是未定义的码位,不应该出现在实际传输中。下表是各种UTF编码的BOM:
UTF编码 ║ Byte Order Mark 
UTF-8 ║ EF BB BF 
UTF-16LE ║ FF FE 
UTF-16BE ║ FE FF 
UTF-32LE ║ FF FE 00 00 
UTF-32BE ║ 00 00 FE FF

上述三种编码方式各有优劣:

其中 UTF-8是变长的,最节省空间的,UTF-32是空间开销最大的。UTF-16空间开销折中,但是有个缺点就是缺少某些字符的编码。

4.乱码:

为什么会出现乱码呢?有两种可能的原因,一种是选择了错误的编码,一种是选择了错误的解码。

但是有的乱码是可逆的,有的是不可逆的。

用 ASCII 进行解码是可逆的。因为ASCII 的256个字符集,都可以用8位二进制数表示。而像 UTF-16这种的,有的二进制表示是没有对应的字符集的,找不到,就是不可逆的。能在字符集找到的就是可逆的。

字符集&各种编码&编码解码的更多相关文章

  1. JavaScript字符集编码与解码

    一.字符集 1)字符与字节(Character) 字符是各种文字和符号的总称,包括乱码:一个字符对应1~n个字节,一字节对应8位,每位用0或1表示. 2)字符集(Character Set) 字符集是 ...

  2. Java 字符集,编码、解码

    1. 计算机中文件.数据底层都是基于二进制的. 计算机底层并没有文本文件.图片文件之分,它只是记录着每个文件的二进制序列. 字符集:包含着字符和二进制序列之间的对应关系,一个字符对应一个二进制序列. ...

  3. java编码原理,java编码和解码问题

    java的编码方式原理 java的JVM的缺省编码方式由系统的“本地语言环境”设置确定,和操作系统的类型无关 . 在JAVA源文件-->JAVAC-->Class-->Java--& ...

  4. 关于Unicode,字符集,字符编码,每个程序员都应该知道的事

    关于Unicode,字符集,字符编码,每个程序员都应该知道的事 作者:Jack47 李笑来的文章如何判断一个人是否聪明?中提到: 必要.清晰.且准确的概念,是一切思考的基石.所谓思考,很大程度上,就是 ...

  5. RapidJSON 代码剖析(三):Unicode 的编码与解码

    根据 RFC-7159: 8.1 Character Encoding JSON text SHALL be encoded in UTF-8, UTF-16, or UTF-32. The defa ...

  6. Android 中的编码与解码

    前言:今天遇到一个问题,一个用户在登录的时候,出现登录失败.但是其他用户登录都是正常的,经过调试发现登录失败的用户的密码中有两个特殊字符: * .#  . 特殊符号在提交表单的时候,出现了编码不一样的 ...

  7. Web开发须知:URL编码与解码

    通常如果一样东西需要编码,说明这样东西并不适合传输.原因多种多样,如Size过大,包含隐私数据,对于Url来说,之所以要进行编码,是因为Url中有些字符会引起歧义. 例如,Url参数字符串中使用key ...

  8. C# Socket编程(3)编码和解码

    在网络通信中,很多情况下:比如说QQ聊天,通讯双方直接传递的都是字符信息.但是字符信息并不能够直接通过网络传输,这些字符集必须先转换成一个字节序列后才能够在网络中传输,于是这里就产生了编码和解码的概念 ...

  9. java中的url 编码与解码

    什么是application/x-www-form-urlencoded字符串? 答:它是一种编码类型.当URL地址里包含非西欧字符的字符串时,系统会将这些字符转换成application/x-www ...

随机推荐

  1. [随笔]利用云虚拟机和学校VPN实现校外访问校内站点(反向代理)

    探究背景简介: 大学校内站点一般不对外开放,个人认为原因有二: 一是站点内容受众就是大学师生: 二是站点基本无防御措施,在公网环境下容易发生意外情况. 至于为何不对外开放,不是这篇随笔探讨的重点,利用 ...

  2. JAVA版A星算法实现

    import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.u ...

  3. PHP面向对象——GD库实现图片水印和缩略图

    今天的实现目标就是使用GD库完成对图片加水印和图 片缩略图两个功能 动身前逻辑准备 属性: 路径 功能: 构造方法 生成水印的方法 获取 图片信息 获取位置信息(123 456 789) 创建图片资源 ...

  4. C语言 一维数组叠加为二维数组样例

    这里参看memcpy的用法,将一个一维整型数组不停的叠加为二维数组 使用宏定义来控制二维数组的行列 代码如下: #include <stdio.h> #include <stdlib ...

  5. 原生js实现轮播图

    原生js实现轮播图 很多网站上都有轮播图,但找到一个系统讲解的却很难,因此这里做一个简单的介绍,希望大家都能有所收获,如果有哪些不正确的地方,希望大家可以指出. 原理: 将一些图片在一行中平铺,然后计 ...

  6. 二cha树

    void porder(BTree *b) { BTree *St[MaxSize],*p; ; if(b!=NULL) { top++; St[top]=b; ) { p=St[top]; top- ...

  7. devexpress设置皮肤、字体以及折叠菜单、伸缩Panel的实现

    1.为了体现系统的个性化,越来越多的系统增加了换肤功能.这里例举一个devexpress实现换肤的案例,效果图对比: 第二张图片中更换了皮肤与字体. 2.皮肤数据源绑定代码: //循环添加皮肤名称 f ...

  8. BZOJ 3925: [Zjoi2015]地震后的幻想乡(概率)

    CLJ就是喜欢出ctsc上讲的东西,看来还是得找时间把他的那几道题做下 首先记f(x)为答案>x的概率,那么把这个东西从0到1积分就是答案了 f(x)<=>边小于x不能使图联通的概率 ...

  9. DP 动态规划

    p1269 马棚 题目: 每天,小明和他的马外出,然后他们一边跑一边玩耍.当他们结束的时候,必须带所有的马返回马棚,小明有K个马棚.他把他的马排成一排然后跟随它走向马棚,因为他们非常疲劳,小明不想让他 ...

  10. 概念 : 过程 : 前台login

    为了解决自动登入.访问权限机制.登入权限而诞生了这过程,过程需要概念来维护记忆. 通过singlePageAutoLoginLocalStorage, UIStateLocalStorage, Use ...