http://f.dataguru.cn/thread-194447-1-1.html
【前言】使用[url=]
oracle[/url]的过程中,出现汉字的时候总是出现乱码现象,疑惑好久。今天决心把字符集的问题彻底搞清楚。下面将通过迭代的方法,由字符集的基本概念逐层深入并过渡到oracle问题上。
1. 字符集1) 常用的字符集:
单字节编码分为:7位编码和8位编码
US7ASCII:单字节7位编码,定义了128个符号,不支持中文。
WE8ISO8859P1:单字节8位编码字符集。
多字节编码分为固定长度编码和变长编码
ZHS16GBK:双字节字符集ZHS16GBK中,编码长度固定,一个中文汉字是一个字符,一个英文字母也是一个字符,所以他们俩占的存储空间一样大,都是两个字节!
GB18030:收录了所有汉字,本标准收录的字符分别以单字节、双字节和四字节编码。
UNICODE:一种集合了所有字符的集合,为变长长度。
UTF-8:UNICODE的一种实现方式,在UNICODE上做了改动,字符分别以1-4个字节编码。
2) UTF-8与UNICODE的深入区别
Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。比如,汉字“严”的unicode是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。
这里就有两个严重的问题:
第一个问题:如何才能区别unicode和ascii?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?
第二个问题:我们已经知道,英文字母只用一个字节表示就够了,如果unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的
为了解决这两个问题,UTF-8就被创造出来了。
UTF-8就是在互联网上使用最广的一种unicode的实现方式。其他实现方式还包括UTF-16和UTF-32,不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8是Unicode的实现方式之一。UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的(一个字节编码)。
2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
下表总结了编码规则,字母x表示可用编码的位。
Unicode符号范围| UTF-8编码方式
(十六进制) | (二进制)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
下面,还是以汉字“严”为例,演示如何实现UTF-8编码。
已知“严”的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此“严”的UTF-8编码需要三个字节,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,从“严”的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,“严”的UTF-8编码是“11100100 10111000 10100101”,转换成十六进制就是E4B8A5。
3) 接下来我们直接、真实的来感受一下字符与编码之间的对应关系
在[url=]
Windows[/url]平台下,有一个最简单的转化方法,就是使用内置的记事本小程序Notepad.exe。打开文件后,点击“文件”菜单中的“另存为”命令,会跳出一个对话框,在最底部有一个“编码”的下拉条。
里面有四个选项:ANSI,Unicode,Unicode big endian和UTF-8。
1)ANSI是默认的编码方式。对于英文文件是ASCII编码,对于简体中文文件是GB2312编码(只针对Windows简体中文版,如果是繁体中文版会采用Big5码)。
2)Unicode编码指的是UCS-2编码方式,即直接用两个字节存入字符的Unicode码。这个选项用的little endian格式。
3)Unicode big endian编码与上一个选项相对应。我在下一节会解释little endian和big endian的涵义。
4)UTF-8编码,也就是上一节谈到的编码方法。
选择完”编码方式“后,点击”保存“按钮,文件的编码方式就立刻转换好了。
然后,用文本编辑[url=]
软件[/url]UltraEdit(很容易下载到的一个文本编辑软件)中的”十六进制功能“,观察该文件的内部编码方式。
1)ANSI:文件的编码就是两个字节“D1 CF”,这正是“严”的GB2312编码,这也暗示GB2312是采用大头方式存储的。
2)Unicode:编码是四个字节“FF FE 25 4E”,其中“FF FE”表明是小头方式存储,真正的编码是4E25。
3)Unicode big endian:编码是四个字节“FE FF 4E 25”,其中“FE FF”表明是大头方式存储。
4)UTF-8:编码是六个字节“EF BB BF E4 B8 A5”,前三个字节“EF BB BF”表示这是UTF-8编码,后三个“E4B8A5”就是“严”的具体编码,它的存储顺序与编码顺序是一致的。
后面部分说了这么多,只是想对字符集有一个更立体的认识,对后续内容的也更容易理解。
4) 乱码问题
将以UTF-8编码的“严”字,转换为US7ASCII编码,因为US7ASCII编码字符集中没有“严”这个字符。于是将“严”转换为目标字符集(US7ASCII)中的一个特殊字符(称为“替换字符”)。比如在这里我们可以将?作为替换字符,所以“严”就转换为了“?”,出现了信息的丢失。这里只是概括的说明一下,后续会针对实际情况进行分析。
2. ORACLE字符集问题
关于oracle应用中的字符集问题,涉及到三个方面:①操作系统的字符集②登陆客户端的字符集③[url=]
数据库[/url]的字符集。下面将依次介绍各自字符集的设定所起到的作用。然后通过[url=]
实验[/url]来进行模拟验证。
1) 文字介绍
操作系统字符集:当你在应用程序中(例如sql*plus)输入数据,此时数据的编码格式是由操作系统的字符集决定。
数据库字符集:我们在创建数据库时,需要考虑的一个问题就是选择什么字符集与国家字符集(通过create database中的CHARACTER SET与NATIONAL CHARACTER SET子句指定)。考虑这个问题,我们必须要清楚数据库中都需要存储什么数据,如果只需要存储英文信息,那么选择US7ASCII作为字符集就可以;但是如果要存储中文,那么我们就需要选择能够支持中文的字符集(如ZHS16GBK);如果需要存储多国语言文字,那就要选择UTF8了。数据库字符集的确定,实际上说明这个数据库所能处理的字符的集合及其编码方式,由于字符集选定后再进行更改会有诸多的限制,所以在数据库创建时一定要考虑清楚后再选择。而我们许多朋友在创建数据库时,不考虑清楚,往往选择一个默认的字符集,如WE8ISO8859P1或US7ASCII,而这两个字符集都没有汉字编码,所以用这种字符集存储汉字信息从原则上说就是错误的。虽然在有些时候选用这种字符集好象也能正常使用,但它会给数据库的使用与维护带来一系列的麻烦,在后面的迭代过程中我们将深入分析。