在工作中经常用遇到乱码问题,特别是之前用过python做一些解析文本的工作的时候。
当时解决问题都是通过上网搜索 错误的提示 按网上的方法修改。很多问题的原因是编码解码方式不统一导致的,但是本身对编码解码了解不甚了解。
故这里对编码方式做一个系统的整理,方便以后遇到问题查阅。本文以记事本的编码方式作为切入点,对编码方式类型做介绍。
如下分成两大类编码进行介绍。
目录
2. Unicode,Unicode big endian,UTF-8
查看文字的编码:汉字字符集编码查询;中文字符集编码:GB2312、BIG5、GBK、GB18030、Unicode
前文:
要弄懂各种编码方式GBK,GB2312,UTF8,UTF16等等 的关键就是两点:
【字符集】和【对字符的编码规则】
字符集即字符的集合,各国语言都有各自的字符(中文,日文,韩文,英文等),计算机中还有一些看不见的控制字符。
字符的编码规则就是存储某个字符的二进制值是多少,即字符和二进制值的映射关系。
那具体有哪些对字符的编码规则呢,首先编码规则是对应于字符集的,比如对于中文字符集,其编码规则有GBK,GB2312等
GBK码所能编码的字符集是中文简体和繁体
GB2312码所能编码的字符集就只有中文简体。
除了能编码的字符集的不同以外,对字符的编码方式也略有不同——GBK和GB2312都是用2个字节表示中文,但是:
GBK编码规则是只要第一个字节大于127,那么直接再往后读取一个字节,这两个字节表示一个汉字,
GB2312编码规则是两个大于127的字节连在一起时表示一个汉字。
UTF8,UTF16编码使用的字符集是Unicode字符集,而Unicode字符集包含了各个国家的语言的字符,不是单一的某种语言的字符。
而我们常说的Unicode码其实并不是一种编码方式,他只是字符与数字(这里的数字并不涉及用几个字节存储等问题,因为不是编码方式)的一种对应关系。
1)Unicode码中字符对应的数字又叫做编码点,字符“a”的Unicode编码点为U+0061,字符“你”的Unicode编码点为U+4F60。字符对应的Unicode编码点由Unicode标准规定,编码使用范围并不超过0x10FFFF。
2)UTF8,UTF16这些才是指编码方式,只不过都是对Unicode码的编码。比如对上述的U+0061,U+4F60这些码如何存储,用几个字节,规则是什么。
下文会介绍。
========================正文===============================
windows下的记事本中的编码方式如下所示:
(下面是win7的记事本,win10的已经是带bom和不带bom的区分分开来了,默认就是不带bom的(后面介绍bom是啥))
可以看到记事本下有这4种编码方式
这里把他们再分成两类进行介绍——ANSI 和 后三者。
1. ANSI
首先介绍 ANSI(不是ASCII!!)编码,不是一种特定的编码方式,具体是要取决于操作系统:
如果是中文操作系统指的ANSI就是指的是 GBK,
如果是日本,就是JIS,
如果是英文,就是ASCII,
……
下面分别介绍一下上面三种ANSI码,首先介绍简单的ASCII码:
-
ASCII码
对于ASCII码在平时用接触到的最多,ASCII对照表规定了字符和其ASCII码(0-127)的对应关系,比如字符字母a对应的ASCII 码是97(十进制)。
具有一定规律,大写字母的ASCII码就是小写字母ASCII码减去32。
每个字符用一个字节(8位)表示。标准ASCII码最高位恒为0,剩下7位(2^7次方=128)。因为128个值就足够表示所有英文字符,数字字符,英文标点字符,和控制字符了。
(ps:ISO8859-1[西欧Latin-1]编码方案把编码扩展到了8位,即256个字符的编码。这种扩展保持了与ASCII的兼容性,即最高位为0的ISO8859-1编码等同于ASCII码。ISO8859-1兼容ASCII码)
-
GBK码
对于GBK码,兼容ASCII码表示的字符以外,延伸0x80-0xff。用2个字节表示一个中文字符。解析的时候,如果第一个字节大于127,直接再往后读取一个字节,显示为对应的中文字符。(GBK码的字符集包含了简体和繁体)。
GBK 兼容 GB2312,GB2312兼容ASCII码)
-
JIS码
字符集肯定就是日文字符了。
所以在不同语种的操作系统下,打开同一个通过ANSI编码的txt,除了ASCII所表示的字符正常显示外,其他字符会有出现乱码的情况,因为解码和编码方式就是不一样的。
可以修改windows的默认语言为日语,即ANSI编码即为JIS编码:
控制面板--》时钟、语言和区域--》更改显示语言==》管理==》非unicode程序语言==》更改区域设置,即当遇到不是Unicode程序的时候,显示文字就使用设置的语言对应编解码方案进行解码。
当修改默认语言为日语以后,为什么记事本、开始菜单什么的还是正确的中文呢?因为它们的unicode程序。就是下面要介绍的unicode。
2. Unicode,Unicode big endian,UTF-8
后面这三种的字符集(charset)都是Unicode,区别在于对于字符对应的数字的存储方式:
在记事本中所谓的“Unicode”其实指的是带 BOM 的UTF16LE,
而Unicode big endian指的是带 BOM 的UTF16BE,
UTF8指的带 BOM 的UTF8。
上面涉及了很多概念,比如Unicode字符集,LE BE ,BOM。下面将介绍 Unicode字符集是怎么规范的和基本的编码方式,为什么区别big endian大端序(BE)和 little endian小端序(LE),以及BOM是什么。
1)Unicode字符集以及编码方式:
Unicode编码点分为17个平面(plane),每个平面包含2的16次(即65536)个码位(code point)。
17个平面的码位可表示为从U+xx0000到U+xxFFFF,其中xx表示十六进制值从00到10(16进制),共计17个平面——
第一个00平面称为“基本多语言平面”(Basic Multilingual Plane,简称BMP)
剩下16个平面(01-10)称为“辅助平面”(Supplementary Planes)。
Unicode标准规定其编码使用范围并不超过0x10FFFF。
所以BMP的平面内的字符的数值 可以用int32的整型来表示。
Unicode字符集中汉字的范围是4e00-9fa5。4e00是汉字"一"
Unicode码就是这样字符与数字的一种对应关系,它并不涉及到如何存储以及传输的问题。比如字符“a”的Unicode码是 U+0061,字符“你”的Unicode码是U+4F60。至于对应的数字的编码方式如下几种:
- UCS-2:
定长编码,固定占用2个字节,它包含65536个编码空间,但0xD800-0xDFFF之间的码位未使用,与Unicode码完全一样(Unicode码中的U+D800-U+DFFF也是保留的)。
- UTF-16:(UTF:Unicode Transformation Format,Unicode转换格式)
变长编码,兼容UCS-2,占用2或4个字节。常用字符同UCS-2,使用2个字节编码。非常用字符,使用四个字节编码,利用0xD800-0xDFFF之间的码位。
Unicode的码位区间为0~0x10FFFF,除“基本多语言平面”(U+000000到U+00FFFF)外,还剩0xFFFFF个码位(并且其值都大于或等于0x10000),均属于辅助平面内的值。
对于“辅助平面”内的字符来说,如果用它们在Unicode中的码位值减去0x10000,则可以得到一个0~0xFFFFF的区间(该区间中的任意值都可以用一个20-bits的数字表示)。所以将该数字的前10位(bits)加上0xD800,就得到UTF-16四字节编码中的前两个字节;该数字的后10位(bits)加上0xDC00,就得到UTF-16四字节编码中的后两个字节,用来表示非常用字符。
例如 D86B DEAB ,这俩是在0xD800-0xDFFF之间的码位,那么一次读取四个字节,按规则反向计算出其Unicode码,即可得到对应字符。
- UCS-4:
USC-4定义了一种31位有效编码位,最高位 为0,其编码固定占用4个字节,编码空间为0x00000000~0x7FFFFFFF(可以编码20多亿个字符)。但实际使用范围并不超过0x10FFFF。
- UTF-32:
编码值与UCS-4相同,只不过其编码空间被限定在了0~0x10FFFF之间。UTF-32是UCS-4的一个子集。
- UTF-8:后面介绍。
2)小端序UTF-16,大端序UTF-16,UTF-8:
1. UTF16:
对于常用字符使用2个字节编码,与Unicode码完全对应,对于非常用字符进行规则计算,用四个字节表示。且在计算机对于字节顺序的理解上的不一致又分成两种大端序和小端序UTF16。
那么何为大端序,小端序?
这里涉及到的是字节顺序,字节序是硬件层面的东西,字节序通常只和你使用的处理器架构有关,而和编程语言无关,比如常见的Intel x86系列就是小端序。
Big-endian(大端序): 数据的高位字节存放在地址的低端 低位字节存放在地址高端(按原来顺序存储)
Little-endian(小端序): 数据的高位字节存放在地址的高端 低位字节存放在地址低端(颠倒顺序存储)
这里指明的存储方式不同也就是影响了存储顺序:
比如写了“a”字符,其Unicode码为U+0061,使用小端序UTF16(颠倒顺序)编码的话——低字节低地址,高字节高地址:
使用大端序UTF16(原来顺序)编码的话,低字节高地址,高字节低地址:
前面的四个字节FFFE,后面介绍。
2. UTF8:
对于英文,如果用UTF16来存储的话整整比用ASCII存储多占用一倍的存储空间(英文字符的Unicode码高字节是0),所以就有了UTF-8。
UTF8是一种变长编码,根据不同的Unicode码值采用不同的存储长度。
那么问题又来了,既然是变长的系统怎么知道几个字节表示一个字符编码呢?对于这类问题计算机中通用的处理方式就是使用标志位,就像ip段的划分一样。具体如下:
字节数 | Unicode码 | UTF-8编码 |
1 | 000000-00007F(最大值7位) | 0xxxxxxx |
2 | 000080-0007FF(最大值11位) | 110xxxxx 10xxxxxx |
3 | 000800-00FFFF(最大值16位) | 1110xxxx 10xxxxxx 10xxxxxx |
4 | 010000-10FFFF(最大值21位) | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
0xxxxxxx,如果是这样的01串,也就是以0开头后面是啥就不用管了XX代表任意bit.就表示把一个字节做为一个单元.就跟ASCII完全一样.
110xxxxx 10xxxxxx.如果是这样的格式,以110开头则把两个字节当一个单元
1110xxxx 10xxxxxx 10xxxxxx 如果是这种格式,以1110开头则是三个字节当一个单元.(正好x的个数是16个)
“你好”的UTF8编码为:你--》11100100 10111101 10100000==》取所有的x,正好是 0100 111101 100000 ,就是“你”的Unicode码U+4F60
总结四种5种编码方式:
对比 | UTF-8 | UTF-16 | UTF-32 | UCS-2 | UCS-4 |
---|---|---|---|---|---|
编码空间 | 0-10FFFF | 0-10FFFF | 0-10FFFF | 0-FFFF | 0-7FFFFFFF |
最少编码字节数 | 1 | 2 | 4 | 2 | 4 |
最多编码字节数 | 4 | 4 | 4 | 2 | 4 |
是否依赖字节序 | 否 | 是 | 是 | 是 | 是 |
3)BOM:
使用记事本编辑TXT文件,在保存的时候,如果是保存为以上三种,则会在最前面保存一个标签。这个标签叫做BOM:
如果是0xFF 0xFE,说明编码用的是UTF16LE,windows默认使用小端序
如果是0xFE 0xFF,说明编码用的是UTF16BE
如果是0xEF 0xBB 0xBF,说明编码用的是UTF-8
可以自行测试一下,在记事本上写上 “联通”,ANSI编码保存,再次打开会是乱码。(在有BOM标签的情况下,肯定能读对。但是按ANSI编码方式存储,不存在BOM标签,只能靠猜,而这里确实通过 符合UTF8 双字节编码的规律,再次用记事本打开的时候把它当成了UTF8进行解码,得到了乱码。因为你不能说前面不带uf8的BOM标签的就不是utf8编码了)
4)notepad++中的编码方式
很全。
参考
聊聊计算机中的编码(Unicode,GBK,ASCII,utf8,utf16,ISO8859-1等)以及乱码问题的解决办法_renwotao2009的专栏-CSDN博客
细说:Unicode, UTF-8, UTF-16, UTF-32, UCS-2, UCS-4 - malecrab - 博客园
ps:
一个文本编辑过程,就包含这按照某种编码规则的编码和解码。
比如ASCII编码: 在文本编辑过程中,按下键盘的a,就会在屏幕上看到a的过程:
在键盘上按下a,计算机收到按键的消息,将a的ascII码进行编码——61H,存储在内存的指定空间。
文本编辑器在内存中读出61H,送到显卡的显存,显卡用ascII码的规则解释显存中的内容——将字符a的图像画在屏幕上。
over 希望对你有帮助, 真正理解 解码编码的含义。如有不清晰或者错误的地方麻烦提出进行修正。