字符集问题一直叫人头疼,究其缘由仍是不能彻底明白其运做原理。sql
在整个运行环节中,字符集在3个环节中发挥做用:数据库
1.软件在操做系统上运做时的对用户的显示,此时采用操做系统定义的字符集进行显示。咱们在系统I/O编程的时候常常要指定字符集,C#中的Text.Encoding=Encoding.Default实际上就是告诉编译器,文本使用系统定义的默认字符集进行编码。sqlplus也是运行在操做系统上的软件,固然要使用系统所指定的字符集对外显示内容。编程
2.数据向oracle服务端传送前的通告。也就是sqlplus告诉服务器如今使用的字符集是什么。windows
3.数据流到达服务器后,按照服务器所使用的字符集自动翻译客户端的数据,而后存储进系统。bash
在客户端sqlplus和服务端传送数据,数据会按照服务端字符集进行翻译,这个过程是自动完成的不须要人工干预。任什么时候候,oracle服务端老是按照本身的字符集设置来存取数据,客户端要想正确显示从服务端读取到的数据,也须要按照本地的字符集设置进行翻译,这个过程也是自动的。服务器
服务器端须要采用合适的字符集进行数据存储,这个很容易理解,ASCII字符集没办法用来存储中文汉字,由于它根本没有描述汉字所须要的编码空间。session
问题经常存在于客户端与服务端通信的过程当中,sqlplus做为运行在操做系统上的软件,不管是显示仍是通信,必然使用操做系统所使用的字符集设置。不管sqlplus设置的字符集,做用只有一个,那就是通告服务器端,为相互之间的字符集翻译作准备。oracle
客户端的字符集设置是在NLS_LANG环境变量中设置的,客户读端的字符集能够和oracle客户端设置得不同(原本人家就是自动翻译的),可是客户端字符集必定要和操做系统设置的字符集相匹配!app
考虑一下,sqlplus使用的是操做系统的字符集定义在显示和发送数据(架设是TYPE_A),却告诉oracle服务器本身使用的字符集是TYPE_B,oracle服务器会怎么办?它会将客户端发送过来的TYPE_A数据看成TYPE_B字符集格式用自身的TYPE_C字符集进行翻译,而后再存储进系统,这就造成了乱码。反向的过程相似,Oracle服务器发出的数据格式没有疑问是TYPE_C,可是客户端软件认为本身使用的编码是TYPE_B并进行了翻译,交给操做系统用TYPE_A字符集总的字符/编码映射关系进行翻译显示,最终致使了没法正确显示。函数
一个现实的例子:RHEL5.8操做系统安装了中文支持包之后,全部的语言环境都被设置成了zh_CN.UTF-8(经过LANG环境变量可知,也可经过locale命令查到),数据库服务器所使用的字符集为ZHS16GBK,很明显,这二者不一致,sqlplus在没有设置NLS_LANG环境变量时,与数据库保持一致,此时,从服务端取得的数据不须要翻译,被sqlplus读取并用zh_CN.UTF-8的字符/编码映射关系进行翻译显示,全部的汉字变成了“?”。
根据上面的分析,要解决这一问题,要把sqlplus的字符集设置成和操做系统一致便可,操做系统设置的是zh_CN.UTF-8,但在.bash_profile里面还不能直接将NLS_LANG设置为zh_CN.UTF-8,由于这个zh_CN.UTF8是字符集的localeID而不是字符集的名称,真正的名称叫SIMPLIFIEDCHINESE_CHINA.AL32UTF8,若是设置成zh_CN.UTF8,oracle会报ORA-12705: Cannotaccess NLS data files or invalid environmentspecified错误。在.bash_profile里面加入NLS_LANG="SIMPLIFIEDCHINESE_CHINA.AL32UTF8"; export NLS_LANG问题就解决了。
下表是locale ID与字符集名称的对应关系:
Language |
Locale ID |
NLS_LANG |
English (American) |
en_US.UTF-8 |
AMERICAN_AMERICA.AL32UTF8 |
English (American) |
en_US.ISO-8859-1 |
AMERICAN_AMERICA.WE8ISO8859P1 |
English (American) |
en_US.ISO-8859-15 |
AMERICAN_AMERICA.WE8ISO8859P15 |
English (Australian) |
en_AU.UTF-8 |
ENGLISH_AUSTRALIA.AL32UTF8 |
English (Australian) |
en_AU.ISO-8859-1 |
ENGLISH_AUSTRALIA.WE8ISO8859P1 |
English (Australian) |
en_AU.ISO-8859-15 |
ENGLISH_AUSTRALIA.WE8ISO8859P15 |
English (British) |
en_GB.UTF-8 |
ENGLISH_UNITED KINGDOM.AL32UTF8 |
English (British) |
en_GB.ISO-8859-1 |
ENGLISH_UNITED KINGDOM.WE8ISO8859P1 |
English (British) |
en_GB.ISO-8859-15 |
ENGLISH_UNITEDKINGDOM.WE8ISO8859P15 |
English (Ireland) |
en_IE.UTF-8 |
ENGLISH_IRELAND.AL32UTF8 |
English (Ireland) |
en_IE.ISO-8859-1 |
ENGLISH_IRELAND.WE8ISO8859P1 |
English (Ireland) |
en_IE.ISO-8859-15 |
ENGLISH_IRELAND.WE8ISO8859P15 |
German |
de_DE.UTF-8 |
GERMAN_GERMANY.AL32UTF8 |
German |
de_DE.ISO-8859-1 |
GERMAN_GERMANY.WE8ISO8859P1 |
German |
de_DE.ISO-8859-15 |
GERMAN_GERMANY.WE8ISO8859P15 |
French |
fr_FR.UTF-8 |
FRENCH_FRANCE.AL32UTF8 |
French |
fr_FR.ISO-8859-1 |
FRENCH_FRANCE.WE8ISO8859P1 |
French |
fr_FR.ISO-8859-15 |
FRENCH_FRANCE.WE8ISO8859P15 |
Italian |
it_IT.UTF-8 |
ITALIAN_ITALY.AL32UTF8 |
Italian |
it_IT.ISO-8859-1 |
ITALIAN_ITALY.WE8ISO8859P1 |
Italian |
it_IT.ISO-8859-15 |
ITALIAN_ITALY.WE8ISO8859P15 |
Spanish |
es_ES.UTF-8 |
SPANISH_SPAIN.AL32UTF8 |
Spanish |
es_ES.ISO-8859-1 |
SPANISH_SPAIN.WE8ISO8859P1 |
Spanish |
es_ES.ISO-8859-15 |
SPANISH_SPAIN.WE8ISO8859P15 |
Spanish (Mexico) |
es_MX.UTF-8 |
MEXICAN SPANISH_MEXICO.AL32UTF8 |
Spanish (Mexico) |
es_MX.ISO-8859-1 |
MEXICAN SPANISH_MEXICO.WE8ISO8859P1 |
Spanish (Mexico) |
es_MX.ISO-8859-15 |
MEXICANSPANISH_MEXICO.WE8ISO8859P15 |
Portuguese (Brazilian) |
pt_BR.UTF-8 |
BRAZILIANPORTUGUESE_BRAZIL.AL32UTF8 |
Portuguese (Brazilian) |
pt_BR.ISO-8859-1 |
BRAZILIANPORTUGUESE_BRAZIL.WE8ISO8859P1 |
Portuguese (Brazilian) |
pt_BR.ISO-8859-15 |
BRAZILIANPORTUGUESE_BRAZIL.WE8ISO8859P15 |
Japanese |
ja_JP.EUC-JP |
JAPANESE_JAPAN.JA16EUC |
Japanese |
ja_JP.UTF-8 |
JAPANESE_JAPAN.AL32UTF8 |
Korean |
ko_KR.EUC-KR |
KOREAN_KOREA.KO16KSC5601 |
Korean |
ko_KR.UTF-8 |
KOREAN_KOREA.AL32UTF8 |
Chinese (simplified) |
zh_CN.GB18030 |
SIMPLIFIEDCHINESE_CHINA.ZHS32GB18030 |
Chinese (simplified) |
zh_CN.UTF-8 |
SIMPLIFIED CHINESE_CHINA.AL32UTF8 |
Chinese (traditional) |
zh_TW.BIG5 |
TRADITIONALCHINESE_TAIWAN.ZHT16BIG5 |
Chinese (traditional) |
zh_TW.UTF-8 |
TRADITIONAL CHINESE_TAIWAN |
步骤能够概括为:1.找到操做系统使用的字符集,并按上表找到对应的字符集名称。2.修改客户端软件的字符集NLS_LANG环境变量设置。
不一样平台的一些细节:
Windows( 如简体系统为:ZHS16GBK,繁体系统为:MSWIN950 )
一、设置session变量
# 经常使用中文字符集
set NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK
# 经常使用unicode字符集
set NLS_LANG=american_america.AL32UTF8
二、能够经过修改注册表键值永久设置
HKEY_LOCAL_MACHINE/SOFTWARE/ORACLE/HOMExx/NLS_LANG
三、设置环境变量
NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK
NLS_LANG=american_america.AL32UTF8
Unix/Linus:
一、设置session变量
# 经常使用unicode字符集
export NLS_LANG=american_america.AL32UTF8
# 经常使用中文字符集
export NLS_LANG="Simplified Chinese_china".ZHS16GBK
三、设置环境变量
能够编辑 bash_profile文件进行永久设置
# vi .bash_profile
NLS_LANG="Simplified Chinese_china".ZHS16GBK
export NLS_LANG
# 使 bash_profile 设置生效
source .bash_profile
其余:
一、查看sqlplus客户编码:
$ echo
$NLS_LANG
二、查看系统编码:
$ locale
三、查看数据库字符集,执行以下查询:
select userenv('language') from dual;
做者:hcling97 http://blog.sina.com.cn/hcling97
2013年5月15日
转载请注明出处
------------------------------------------------------------------------------------------------------------------------
2. 客户端操做系统字符集:即客户端操做系统以哪一种字符编码存储字符。
若是是Windows,可使用chcp命令得到代码页(code page):
根据该代码页,到微软的官方文档《National Language Support (NLS) API Reference》找到其对应的字符集。
若是是Linux,字符集在/etc/sysconfig/i18n设置:
3. 客户端NLS_LANG参数:该参数用于向Oracle指示客户端操做系统的字符集。
有了以上3个基本概念以后,我来阐述一下Oracle字符集转换的基本原则:
几种常见状况分析
下面先看一个例子,再透过现象看本质,咱们会针对这个例子进行分析。
该例子以下:
上面例子看起来很诡异,session1和2都能正常显示本身插入的字符串,又都不能正常显示对方插入的字符串。为了弄清楚,咱们首先得知道数据库里对这两个字符串是怎么存储的。咱们可使用dump函数得到字符在数据库的编码:
根据AL32UTF8的编码,“中国”两字的正确编码为(都为3个字节):
中--e4,b8,ad
国--e5,9b,bd
所以session 1插入的字符串在数据库中的编码是错误的,session 2正确。这也是为何必定要设置NLS_LANG为客户端操做系统的字符集。
可是根据上面实验咱们能够知道,数据库中存储正确,并不表明客户端能正常显示;一样地,即时数据库没有正确存储,有时候客户端也可以正常显示,这又是为何呢?别急,请听我慢慢道来:
场景1:session 1插入,session 1查询,在数据库中存储错误,但显示正确。
插入过程:
”中国“两字在客户端操做系统字符集ZHS16GBK中的编码是”d6,d0,b9,fa",因为NLS_LANG和数据库字符集相同,数据库端对客户端传过来的字符编码不进行任何转换直接存入数据库,所以数据库中存储的编码也是“d6,d0,b9,fa”,
读取过程:
数据库端读取的编码是“d6,d0,b9,fa”,因为NLS_LANG和数据库字符集相同,客户端对数据库端传过来的字符编码不进行任何转换直接显示,编码”d6,d0,b9,fa“在客户端操做系统字符集ZHS16GBK对应的汉字为“中国”。
从以上分析可知,虽然读取时正确的,但那是由于负负得正,实际上数据库中存储是错误的,所以要特别当心这种状况,在生成库中要避免。其实只要对它进行length操做就能轻易揭开它的假面具:
得出的长度竟然为3!实际的长度只是2,这会带来不少麻烦。
场景2:session 1插入,session 2查询,在数据库中存储错误,显示也错误。
插入过程和场景1同样,这里就再也不累述。
读取过程:
数据库端读取的编码是“d6,d0,b9,fa”,因为NLS_LANG和数据库字符集不一样,客户端对数据库端传过来的字符编码进行转换,数据库端字符集AL32UTF8里编为“d6,d0,b9,fa”没法在客户端操做系统字符集ZHS16GBK里找到对应的编码,因此只好用?代替。
场景3:session 2插入,session 1查询,在数据库中存储正确,但显示错误。
插入过程:
”中国“两字在客户端操做系统字符集ZHS16GBK中的编码是”d6,d0,b9,fa",因为NLS_LANG和数据库字符集不一样,Oracle会进行字符编码转换,也就是将字符集ZHS16GBK里“中国”的编码“d6,d0,b9,fa"转换为字符集"AL32UTF8"里”中国“的编码”e4,b8,ad,e5,9b,bd“。
读取过程:
数据库端读取的编码是”e4,b8,ad,e5,9b,bd“,因为NLS_LANG和数据库字符集相同,客户端对数据库端传过来的字符编码不进行任何转换直接显示,编码”e4,b8,ad,e5,9b,bd“在客户端操做系统字符集ZHS16GBK对应的汉字为“涓 浗”(本来2个字符,如今变成了3个字符,由于ZHS16GBK的汉字以2个字节编码)。
场景4:session 2插入,session 2查询,在数据库中存储正确,显示也正确。
插入过程和场景3相似。
读取过程:
数据库端读取的编码是”e4,b8,ad,e5,9b,bd“,因为NLS_LANG和数据库字符集不一样,客户端对数据库端传过来的字符编码进行转换,数据库端字符集AL32UTF8里”中国“两字的编码”e4,b8,ad,e5,9b,bd“转换成客户端操做系统字符集ZHS16GBK里“中国”两字的编码“d6,d0,b9,fa",并正常显示。
这种状况虽然通过了两次转换,都确实最正确、最推荐的方式。
附录:Oracle 字符集超集和子集的对应关系可查看:http://download.oracle.com/docs/cd/B19306_01/server.102/b14225/applocaledata.htm#sthref1988
结论:NLS_LANG只和客户端操做系统的字符集相关,若是客户端操做系统的字符集和数据库字符集间没法正确转换,则应该首先改变客户端终端的字符集,而不是简单地把NLS_LANG设为和数据库字符集同样。
来源:http://blog.csdn.net/dbanote/article/details/9158367,做者:朱显杰