应用程序中的编码问题让人头疼,一直是这样,今天下午就被数据库编码错误搞的头疼不已。
那么,就决心好好总结一下编码带来的问题,争取让自己对整个编码体系有一个清晰的认识。
从编码问题的产生说起
我们知道,计算机是美国人发明的,人家的英语体系总从来就只有26个英文字母和一些数字、特殊字符等,为了储存文字信息,于是使用了最早的ascii码进行字符编码。而后来由于计算机的普及,多国语言文字变得重要起来,于是多语言的特性成为了计算机的必备,各国进行各国的国家标准编码,中国的便是GB2312(1980年),而后1995年又颁布了《汉字编码扩展规范》(GBK),GBK与GB2312相兼容,但又增加了一些兼容汉字,方便了和Big5码等进行转换。这套GBK编码,逐渐成为了中国计算机的主流编码。
Unicode字符集和UTF-8编码
随着计算机的发展,往往一款软件不但要兼容一个国家的语言,还要兼容许多国家的语言,尤其是亚洲国家中日韩三国,光中文常用字符就有7000,所以要解决这么多文字的编码问题,就需要用更多规模的表示,Unicode是一个巨大的字符集,囊括了世界各地的语言,这样编码就更为统一和方便了。但Unicode并没有规定这些字符具体应该怎么样在计算机中存储,虽然字符集是全的,但计算机中要兼顾效率和方便等问题,UCS-2是其中的一种常用方案,采用两个字节来编码一个字符,这和ascii码不兼容,而且还有一个大问题,UCS-2并不能表示全部的汉字,汉字的简繁体加起来总共有六七万,UCS-2只有65536个编码,根本存不下,所以只收入了大部分常用字。也有表示所有汉字的方案UCS-4,不过4个字节用来存储汉字,效率就较为低下。
UTF是“UCS Transformation Format”的缩写,是Unicode字符集的一类高效实现方式。
UTF-8是我个人比较喜欢的一种编码方式,它是一种针对Unicode的可变长度字符编码,又称为万国码。它使用1-6个字节进行编码,最前面128个编码内容和ascii码兼容,所以我们用UTF-8编写纯英文文本是和ascii码几乎一样的。(注意我说的是几乎)。
如果UNICODE字符由2个字节表示,则编码成UTF-8很可能需要3个字节。而如果UNICODE字符由4个字节表示,则编码成UTF-8可能需要6个字节。用4个或6个字节去编码一个UNICODE字符可能太多了,但这样的UNICODE字符往往是生僻字,极为少见。
这种编码的思想和哈夫曼编码是类似的,将高频字符缩短,低频字符变长,使得整体的编码效率更优。
我以前有段时间,就经常将Unicode和UTF-8搞混了,Unicode是字符集,网上不严格的情况也指Unicode字符集的常用编码UCS-2,也就是用两个字节编码的Unicode码。
一些常见系统的编码:
VC 6.0 Ascii码
VS2008 - 2013 UCS-2(开启Unicode后)
Windows中文版 GBK
Linux(andorid) UTF-8
Mac OX UTF-8
JAVA UCS-2
Python2 Ascii (byte)
Python3 UCS-2
注:操作系统所述都是默认编码,这三种主流操作系统的系统编码都是可以更改的。
MBCS(Multi-Byte Character Set)和内码表(codepage)
再介绍两个字符集中较为深入的概念,MBCS和CodePage。
MBCS(Multi-Byte Chactacter System,即多字节字符系统),是所有多字节编码方案的总称,MBCS 编程主要用于为国际市场编写的应用程序。由于往往是针对一国市场只有一种文字,那么为了节约资源,往往将这些文字用双字节或尽量少的字节的方式进行保存。
因为这些双字节文字和ANSI是混和在一起的,为了加以区别,Windows将这些字符的最高位置为1(即这些双字节文字的每个字节都>=127),所以这种表示法可以表示 127x127 约一万多种非ANSI文字 ,其本上可以表示任何一种语言的常用文字了。于是,Windows为每一个区域版本,都制定了分别独立的文字编码,这就是MBCS(多字节码)。
而这些分页后的编码方式,都被保存成了不同的CodePage(内码表,这里内码的意思是机器内部编码,相对于外码,外部输入文字用的编码,例如拼音、五笔、郑码等),例如中文就是大家熟知的CP936。要注意,这种编码方式是早期windows独有的,由于使用较早,应用也十分广泛,而CP936和GB2312-80在编码上则是几乎一样的(此处见Wiki百科——汉字内码扩展规范),后来扩展GBK后,CP936也进行了同样的扩展。
此技术的使用最早追溯到MS-DOS3.3(1987年4月发行)向IBMPC用户引进了内码表的概念,Windows也使用此概念。
内码表成为了系统进行多语言编码及转换的重要工具。当然,Unicode码也被收入CodePage中。
说白了,MBCS和CodePage就是计算机解决多字节编码问题的通用手段,各种区域编码可以在其中找到对应编码的映射的,Unicode和UTF-8也是可以在其中找到对应的映射的。
常见的CodePage
932 —日文
936 —简体中文(GBK)
949 —韩文
950 —繁体中文(大五码)
1200 —UCS-2LE Unicode 小端序
1201 —UCS-2BE Unicode 大端序
1252 —西欧拉丁字母ISO-8859-1.
65000 — UTF-7 Unicode
65001 — UTF-8 Unicode
计算机是如何显示文字的呢?
计算机要对文字进行存储后就需要显示出来,而我们的液晶屏都是一个个的像素点组成的,这就必须要对文字进行渲染绘制,发送到显卡中进行栅格化和显示等操作。
Dos下最简单,利用主板BIOS就能对ascii码进行点阵化输出。简单的我们就不再多谈,windows下是如何对文字进行绘制显示的呢?
字库
我们都知道,显示字体除了要有文字的编码,还需要有显示用的字的样式,这就是 字库,windows下的fonts文件夹大家应该都十分熟悉,更改或删除字库就是就是文件的移动而已。而字库实际上也有不同的种类的,构建原理也不都相同。
最早的字库是点阵字库,这种字库看起来和黑白图片貌似没有什么大区别,就是记录像素是黑还是白,我们用windows自带的字库编辑程序,就可以处理这种字库。
这种字库的缺点也是显而易见的,首先是缩放不易,在小字体和大字体显示时都容易失真走样,而且占用空间较大。
矢量轮廓字库,这种字库是用的矢量图原理进行存储的,将外部边界抽象成数学上的矢量线段,可以方便的缩放旋转等操作。缺点则是连续性不好,放大后就变出行了折痕,效果不够理想。
曲线轮廓字库,这是通过直线和曲线段共同构造文字的方法,往往使用Bezier曲线对轮廓进行拟合,效果非常好,字体平滑美工,但惟独就是计算较为费时间。
TrueType字形技术
TrueType采用几何学中二次B样条曲线及直线来描述字体的外形轮廓。由一些数学算法进行对应大小字体的生成,无论放大或缩小,字符总是光滑的。
TrueType字体与PostScript字体、OpenType字体是主要的三种计算机矢量字体(又称轮廓字体、描边字体),后缀.ttf。
虽然计算较快,但相比于PostScript字体,质量要差一些,特别是文字太小时,不够清晰。
所以字库一般会对小字体和常用打印字号制作对应的点阵字库,保证其精度,其他情况则使用TrueType字体。
WOFF–WebOpen Font Format (.woff)
WOFF(Web开发字体格式)是一种专门为了Web而设计的字体格式标准,实际上是对于TrueType/OpenType等字体格式的封装,每个字体文件中含有字体以及针对字体的元数据(Metadata),字体文件被压缩,以便于网络传输。
其实大家就会发现了,网络上很火的图标字体,实际上就是根据最基础的字库技术生成出来的。
字体的显示流程
介绍到这里,大家应该对整个字体的绘制过程有个整体的认识了,恩,首先是通过
首先是经过字符编解码,将硬盘中存储的有对应编码的文本文件进行加载,例如Java的文件IO,变成内存中的字符串对象,也就是符合本语言字符串存储特性的数据,(当然如果你愿意,也可以当做二进制读入,然后再手段转换编码,也是可以的),绘制时则首先调用对应的CodePage进行编码的索引查找,找到对应的字体字形索引,然后根据字形索引获取到字库中的数据,根据系统提供的绘制曲线绘制样条线的方法进行图形渲染,这样就能得到显示器上的图像了。而更高级的就是对字体进行反走样,锐化,等更为细节的操作了。
编码问题
编码问题解决起来实际上很简单,就是统一编码呗,所有需要注意的地方都有统一制定编码格式,这样,程序自然就会按照制定的编码进行操作。
Web页面编码
做网站时会用到大量的用户界面,这些用户界面的编码就是整个页面编码的核心,假若你用的是php,那么你肯定要在页面输出时,使用header函数,制定http返回的页面编码。
例如这样:
header("Content-type: text/html; charset=utf-8");
这样做的底层含义实际上是让html通信协议的报头指定了编码格式。
而拥有同样功能的meta标签
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
实际上也是让http协议发送时指定编码格式,防止乱码的方法。
资源文件编码
很多人认为指定了文件编码方式,就不会发送乱码了,真的是这样吗?显然不对,因为你要注意,你的html文件是怎么存的呢?假若你在用一个文本编辑器将html的内容保存成了gbk编码,虽然你在文本编辑器中是正常的,但由于你的文件存的编码和html指定的解码方式不统一,必然会导致乱码。
所以乱码问题必须时刻注意,文件的本身储存格式决定了其编码格式和声明的解码格式。而且更为让人难以捉摸的是,php的编码还有很多要注意的地方,比如php如果当做类代码来用时,php程序的编码并不是那么重要,但如果你在程序中定义中文字符串,或者将php当做模板引擎和html混排时,那php本身的编码和输出的编码都要注意,哪部分编码和解码的格式不符合,哪部分就会乱码。
而且使用php存取文件时尤为注意,资源文件的格式,是会影响编码的,如果你是按照字符串的方式存取还好说,但如果是用二进制存放时,则文本的编码是不会被自动转换的,所以要将这类资源文件的文件编码手动设置好。
数据库编码
数据库编码是十分重要的,我们在使用数据库时,往往会在创建数据库时,就指定好其默认的编码格式,以防乱码,但即使是这样,数据库往往也不会很听话的工作,因为有一个细节很难被注意到,那就是数据库SQL语句的编码。
例如:
<property name="url" value="jdbc:mysql://localhost:3306/email?useUnicode=true&characterEncoding=UTF-8" />
这样操作的最主要目的,就是不管数据库内部编码是什么样的,但字符串的编码格式在连接层会被统一,保证发过去的数据时utf-8的,返回时也是utf-8的,这样即使数据库本身存储编码不是utf-8,都不会有影响。