理清文本编码
最近搞u3d开发,把一些文本文件打成包,发布到手机上,发现各种乱码,结果法线是各种编码方式不对,于是好好研究了一下不同平台上的字符编码,然后发现以前一直认为的unicode utf8 gbk asc2这些字符编码自以为很懂,其实完全理解的不对,在这方面也是因为有太多的网上文档在误人子弟,这里好好梳理一下
1.首先为什么要对字符进行编码?文本格式同二进制格式有何不同?
计算机的基本计量单位是一个比特(即一个二进制数),而存储数据的一个基本单位是字节(8位二进制数,2个16进制数 0X00-0xff),也就是说一切数据在磁盘上都是用一个个字节存储的,但是文本字符确实另外一个概念,字节(0X00-0xff)是不能天然表示文本字符的,因为字符太多了,所以需要使用编码方式(用几个字节的组合)来表示不用的文本字符。这是对字符进行编码的原因
二进制文件就是一个字节的序列。你用不同的解码去解释他可能得到不同的意义,有可能解释出来就是一张jpeg图片也说不定。但是早读写文件时标记为文本文件,就等于告诉了读写程序(例如fstream这些玩意儿),你要按照文件的解码方式去解释这些二进制,返回给我的直接就是解释好的文本字符,而不是最原始的字节,比如在字节5A 二进制读入就是5a(或者十进制的byte表示成90),但是如果用文本格式读(用字符编码区解释)就可能得到的是 ‘Z’。
2.什么是字符编码?
既然需要对文本字符进行编码,就需要有标准,因为历史和其他种种原因,出现了很多编码标准,没有一个全世界唯一认同的编码标准,比如你定义10代表a,我可以定义1000代表a。所以不同的字符编码唯一的区别就是字符的编码值(表示那个字符的多个字节的数值)不同。例如asc2定义z的值为90,没有定义汉字字符,unicode定义0x6211这个数代表‘我’这个汉字。不同的字符值就是不同字符编码体系的体现。
现在常用的字符编码体系主要有asc2和unicode,gbk ,bigend5等,而unicode是完全囊括了asc2的,因为unicode可以说是国际标准,而GBK是大陆的标准,这些不同的编码体系之间通常没有太多的直接联系或者公式好换算,最直接的方法就是查映射表,例如,'我‘在unicode规定的值是0x6211,而在gbk里面是0xced2.值完全不同。
一句话,不同的字符编码集定义表示一个文本字符不同的数值
3.Unicode和utf-8的区别是什么?
首先一种编码集可能只规定了某个字符的值应该是多少,但是对这个值的存储(在传输意义上的)没有规定,所以一个字符编码下可能有不同的实现方式,因此同一个编码集下对同一个字符的存储并不一样,例如同是‘我’,在unicode编码上一定是值0x6211,但是我可以用2个字节存储,也可以用四个字节存储,甚至我可以对某个字符用1个字节存储,某些用多个,因此这就有了一个在同一个编码集下不同是存储实现的问题。
在unicode里,有很多种存储实现方式,包括UTF-8 UTF-16 UTF-32(也有直接用unicode表示utf-32) ,等等,他们对同一个字符都用同一个值表示,但是UTF-16对每个字节会用2个字节存,UTF-8用变长字节存。例如0x00AB和0xAB读进来的值都是一样的,也就是表示的一个字符,但是确实两种不同的存储方式,如果你用UTF-8解码,就要按照UTF-8的定义去读字节,然后得到数值,如果你用UTF-16就要一次读两个字节,然后得到数值。他们的存储方式不同,文件的内容大小肯定不一样,而目前UTF-8因为变长存储的原因,节省存储空间,一般是比较流行的。
所以这里就明白了unicode和utf-8明显不是一个平行的关系,而是UTF-8只是unicode字符编码集中的一种存储实现方式。而unicode和gbk却是平行的概念。
一句话:实现一种字符编码,首先要定义它的字符值,然后定义这个字符值的字节存储方式
4.codepage 和cp936这些编码是啥玩意儿?
这些都是微软自己定义的概念。因为存在这么多种字符编码集,每种编码集里面又可能有不同的存储方式,在写程序上再不同的存储方式的文件中解码编码转换就是个麻烦事,比如你要把一个gbk编码的文件读出来转换成utf-16来存储,怎么办?你首先要用gbk的解存储格式的方法读入文件的每个文本字符的数值,然后查gbk的编码表,得到每个数值表示的文本,得到文本后,要存储,首先就要查utf-16所属的unicode的编码表,得到每个字符在unicode下面应该表示的数值,最后用utf-16的存储方式存储每个数值,完成。
而微软为了能够在存储方式上统一不同的编码方式,就按照不同编码的不同存储方式为基本单元重新定义一个code page,在这个code page上,UTF-8 UTF-16 GB2312等都是不同的code page,而unicode则没有相应的codepage,因为他不是一个具体的存储方式,gbk也没有被收录,因为gbk不是官方标准,官方的简体中文编码集是GB2312,它的code page序号是936,因此所说的cp936即是值微软定义的GB2312文本字符编码(和存储方式)。
5.平台支持和默认编码
一般在简体中文的windows上,默认的编码就是GB2312(CP936),在linux上一般是utf-8,所以像android ios这些类unix的系统的默认编码都是utf-8,而windows也支持UTF-8的编解码,但是很多类unix系统确不一定支持GB2312或者GBK这样的编解码(例如android和ios),所以UTF-8一般更加的通用,是一些跨平台时通常选用的编码。
昨天遇到的问题就是一个带中文的汉字直接打包到ios上乱码,问题就在于汉字生成的时候使用简体中文windows的默认编码GB2312,到ios上面打开,ios默认的编码是UTF-8,当然解析到乱码,所以我们的项目在打包时统一转成UTF-8编码,在设备上(pc android ios)打开再统一用UTF-8解码,问题就解决了