Oracle中文乱码问题的解决

接触Oracle的人想必都会遇到中文乱码的问题,网上讲的很多,大部分都是别人的文章抄来抄去,没有自己去验证分析过,又或许只是特殊情况下,解决了就放罢,随手做了笔记,以为这就是解决的方法,下次在碰到却不一定管用

一些基础知识(从网上抄的 写的不错):

都有哪些字符集?

ASCII    7位
字符集: 包括的字符有:数字,大小写字母,分号、换行之类的符号,编码方式是用一个7bit表示一个字符 (A的编码是65,b的编码是98)。ASCII只规定了英文字母的编码,非英文语言不能用ASCII编码表示,为此,不同的国家,都为自己的语言做了编码。这就有了字符集第一步的扩展.

ISO-8859   8位
英文和欧洲其他语言的单字节字符集(SingleByte Charsets)
ISO(国际标准化组织)制定了一个针对欧洲多国的字符集标准系列ISO-8859,可以将该系列的字符集都想象成一个:2^8 = 16 * 16 = 256个格子的棋盘,这样所有的西文字符(英文)用这样一个16×16的坐标系就基本可以覆盖全了。而英文实际上只用其中小于128(x80)的部分就够了。利用大于128部分的空间的不同定义规则形成了针对其他欧洲语言的扩展字符集:ISO-8859-2 ISO-8859-4等。不同国家使用复合自己国家语言的字符集标准。所有这些国家的字符集都有公共交集ASCII。
oracle 7.3.4这样老的系统中,很常见的就是WE8ISO8859P1(Western European 8-bit ISO 8859 Part 1)。记得即使中文系统好多也用的这个,忘了是不是默认值了。

GB2312和GBK    16位
汉字这么多,用这么一个256格的小棋盘肯定放不下,所以要区别成千上万的汉字解决办法就是用2个字节(坐标)来定位一个“字”在棋盘上的位置,将以上规则做一个扩展:
* 如果第1个字符是小于128(x80)的仍和英文字符集编码方式保持兼容;
* 如果第1个字符是大于128(x80)的,就当成是汉字的第1个字节,这个自己和后面紧跟的1个字节组成一个汉字;
其结果相当于在位于128以上的小棋格里每个小棋格又划分出了一个16×16的小棋盘。这样一个棋盘中的格子数(可能容纳的字符数)就变成了128 + 128 * 256。按照类似的方式有了简体中文的GB2312标准,繁体中文的BIG5字符集和日文的SJIS字符集等,GB2312字符集包含大约有六仟多个常用简体汉字。所有这些从ASCII扩展式的编码方式中:英文部分都是兼容的,但扩展部分的编码方式是不兼容的,虽然很多字在3种体系中写法一致(比如“中文”这2个字)但在相应字符集中的坐标不一致,所以GB2312编写的页面用BIG5看就变得面目全非了。而且有时候经常在浏览其他非英语国家的页面时(比如包含有德语的人名时)经常出现奇怪的汉字,其实就是扩展位的编码冲突造成的。我把GBK和GB18030理解成一个小UNICODE:GBK字符集是GB2312的扩展(K),GBK里大约有贰万玖仟多个字符,除了保持和 GB2312兼容外,繁体中文字,甚至连日文的假名字符也能显示。而GB18030-2000则是一个更复杂的字符集,采用变长字节的编码方式(2~4字节),能够支持更多的字符。
oracle8,8i中,中文环境默认是ZHS16GB2312?还是ZHS16GBK?谁比较清楚滴?

UNICODE    16位
ASCII(英文) ==> 西欧文字 ==> 东欧字符集(俄文,希腊语等) ==> 东亚字符集(GB2312 BIG5 SJIS等)==> 扩展字符集GBK GB18030这个发展过程基本上也反映了字符集标准的发展过程,但这么随着时间的推移,尤其是互联网让跨语言的信息的交互变得越来越多的时候,太多针对本地语言的编码标准的出现导致一个应用程序的国际化变得成本非常高。尤其是你要编写一个同时包含法文和简体中文的文档,这时候一般都会想到要是用一个通用的字符集能够显示所有语言的所有文字就好了,而且这样做应用也能够比较方便的国际化,为了达到这个目标,即使应用牺牲一些空间和程序效率也是非常值得的。UNICODE就是这样一个通用的解决方案。你可以把UNICODE想象成这样:让所有的字符(包括英文)都用2个字节(2个8位)表示,这样就有了一个2^(8*2) = 256 * 256 = 65536个格子的大棋盘。在这个棋盘中,这样中(简繁)日韩(还包括越南)文字作为CJK字符集都放在一定的区位内,为了减少重复,各种语言中写法一样的字共享一个“棋格”。

UTF-8    可变字长
毕竟互联网70%以上的信息仍然是英文。如果连英文都用2个字节存取(UCS-2),空间浪费不就太多了?所谓UTF-8就是这样一个为了提高英文存取效率的字符集转换格式:Unicode Transformation Form 8-bit form。用UTF-8,UNICODE的2字节字符用变长个(1-3个字节)表示:1. 对英文,仍然和ASCII一样用1个字节表示,这个字节的值小于128(x80);  2. 扩展的ASCII字符(主要是西欧),第一字节用C2 – DF之间的范围,双字节表示。3.对其他语言,比如亚洲语系,还有各种特殊符号,使用3个字节表示;
因此,在应用中程序处理过程中所有字符都是16位(双字节),但在存取转换成字节流时使用UTF-8格式转换,对于英文字符来说和原来用ASCII方式存取时相比大小仍然是一样的,而对中文来说和原来的GB2312编码方式相比,大小为:(3字节/2字节)=1.5倍
ps:UTF8标准也在变化。最新的标准,UTF8可以是1-4个字符。4个字符是为了将来可能的新字符,符号进行扩展。(用10g文档上来说,就是:historic characters; musical symbols; mathematical symbols)为此oracle也定义了新的字符集AL32UTF8,也就是说其实UTF8的国际化标准就一个,只是版本在变化。而oracle公司根据不同国际标准版本有了UTF8和AL32UTF8两个具体在数据库上实现的字符集。UTF8不支持4字节扩展的。
总结:对于不同字符集关系的理解,我们可以用个比喻来说明。如果说一个字符集就是一个公司的仓库,字符的编码就是货架编号,字符的实际值就是货物本身。那么:
ASCII就是最小的仓库,就是127个货架,放了127样货物,比如 100号货架上放的是雨伞;
西欧扩展iso-8859系列,这些仓库都比A记(ASCII)多了差不多一倍的货物,而且编号1~127的货物也和A记一模一样。不过如果你去看160号货架的话,可能这家放的茶杯,那家就是毛巾了;
ZHS16GBK,这是个百货公司,有上万的货物,即是如此还是没有忘本,你如果找100号货架,肯定还是能拿到雨伞;
UNICODE,他的口号是别人有的我也要有。所以修了有65535个货架的大仓库。虽然Z记有的东西他也全有,不过货物放的货架(编码)全不一样了。
UTF8,真正的托拉斯,不但做到了人无我有(包括了现在所有的字符集的字符),还想千秋万代一桶浆糊(为以后扩展留了余地),和U记类似,东西非常多,货物编号除了1~127号全和别人不一样了。

oracle如何选择字符集?

我们在创建数据库时,需要考虑的一个问题就是选择什么字符集与国家字符集(通过create database中的CHARACTER SET与NATIONAL CHARACTER SET子句指定)。考虑这个问题,我们必须要清楚数据库中都需要存储什么数据,如果只需要存储英文信息,那么选择US7ASCII作为字符集就可以;但是如果要存储中文,那么我们就需要选择能够支持中文的字符集(如ZHS16GBK);如果需要存储多国语言文字,那就要选择UTF8了。

oracle出现中文乱码和以下几个因素相关
1、oracle服务器编码
oracle软件安装时就会让你选择编码,这时的编码便是服务器编码,我们创建的数据库,数据表,里面所有存储的中文都将默认使用这个编码
查看方法如下:
Select userenv(‘language’) from dual;                       或者               Select name, value$ from props$;
select * from nls_database_parameters where parameter=’NLS_CHARACTERSET’;           当前数据库的字符集
2、Oracle客户端编码
window
注册表里面的NLS_LANG 位置:HKEY_LOCAL_MACHINE->SOFTWARE->Oracle->NLS_LANG
Linux
环境变量NLS_LANG 所在文件 家目录下的.bash_profile
3、客户端服务器的字符集
Linux需查看/etc/sysconfig/i18n
Windows环境变量NLS_LANG

这三个字符类型要一致,否则就会出现乱码,经常也有客户端和服务器端字符集是一致的,但是还是显示乱码,此时就要看看,本地服务器字符集是什么了

 

 

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注