字符集问题一直叫人头疼,究其原因还是不能完全明白其运作原理。

在整个运行环节中,字符集在3个环节中发挥作用:

1.软件在操作系统上运作时的对用户的显示,此时采用操作系统定义的字符集进行显示。我们在系统I/O编程的时候经常要指定字符集,C#中的Text.Encoding=Encoding.Default实际上就是告诉编译器,文本使用系统定义的默认字符集进行编码。sqlplus也是运行在操作系统上的软件,当然要使用系统所指定的字符集对外显示内容。

2.数据向Oracle服务端传送前的通告。也就是sqlplus告诉服务器现在使用的字符集是什么。

3.数据流到达服务器后,按照服务器所使用的字符集自动翻译客户端的数据,然后存储进系统。

在客户端sqlplus和服务端传送数据,数据会按照服务端字符集进行翻译,这个过程是自动完成的不需要人工干预。任何时候,oracle服务端总是按照自己的字符集设置来存取数据,客户端要想正确显示从服务端读取到的数据,也需要按照本地的字符集设置进行翻译,这个过程也是自动的。

服务器端需要采用合适的字符集进行数据存储,这个很容易理解,ASCII字符集没办法用来存储中文汉字,因为它根本没有描述汉字所需要的编码空间。

问题常常存在于客户端与服务端通讯的过程中,sqlplus作为运行在操作系统上的软件,无论是显示还是通讯,必然使用操作系统所使用的字符集设置。无论sqlplus设置的字符集,作用只有一个,那就是通告服务器端,为相互之间的字符集翻译做准备。

客户端的字符集设置是在NLS_LANG环境变量中设置的,客户读端的字符集可以和oracle客户端设置得不一样(本来人家就是自动翻译的),但是客户端字符集一定要和操作系统设置的字符集相匹配!

考虑一下,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 )

1、设置session变量


# 常用中文字符集
set NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK

# 常用unicode字符集
set NLS_LANG=american_america.AL32UTF8

2、可以通过修改注册表键值永久设置
HKEY_LOCAL_MACHINE/SOFTWARE/ORACLE/HOMExx/NLS_LANG

3、设置环境变量
NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK
NLS_LANG=american_america.AL32UTF8

Unix/Linus:

1、设置session变量

# 常用unicode字符集
export NLS_LANG=american_america.AL32UTF8

# 常用中文字符集
export NLS_LANG="Simplified Chinese_china".ZHS16GBK

3、设置环境变量
可以编辑 bash_profile文件进行永久设置
# vi .bash_profile
NLS_LANG="Simplified Chinese_china".ZHS16GBK
export NLS_LANG
# 使 bash_profile 设置生效
source .bash_profile

其他:

1、查看sqlplus客户编码:
$ echo

$NLS_LANG

2、查看系统编码:
$ locale

3、查看数据库字符集,执行如下查询:
select userenv('language') from dual;

作者:hcling97    http://blog.sina.com.cn/hcling97

2013年5月15日

转载请注明出处

------------------------------------------------------------------------------------------------------------------------

万国码(Unicode):包含了几乎人类所有可用的字符,每年还在不断的增加,可以看作是一种通用的字符集。它将全世界所有的字符统一化,统一编码,不会再出现字符不兼容和字符转换的问题。它有以下三种编码方式:
  1. UTF-32编码:固定使用4个字节来表示一个字符,存在空间利用效率的问题。
  2. UTF-16编码:对相对常用的60000余个字符使用两个字节进行编码,其余的使用4字节。
  3. UTF- 8编码:兼容ASCII编码;拉丁文、希腊文等使用两个字节;包括汉字在内的其它常用字符使用三个字节;剩下的极少使用的字符使用四个字节。
 

Oracle字符集基本原理

 
在搞懂Oracle字符集基本原理之前,一定要先分清以下三个概念:
 
1. Oracle数据库服务器字符集:即Oracle以哪种字符编码存储字符,可以通过以下语句查出数据库字符集的设置。
  1. SQL> select * from v$nls_parameters where parameter='NLS_CHARACTERSET';
  2. PARAMETER                      VALUE
  3. ------------------------------ -----------------
  4. NLS_CHARACTERSET               AL32UTF8

2. 客户端操作系统字符集:即客户端操作系统以哪种字符编码存储字符。

如果是Windows,可以使用chcp命令获得代码页(code page):

  1. C:\Users\xianzhu>chcp
  2. Active code page: 936

根据该代码页,到微软的官方文档《National Language Support (NLS) API Reference》找到其对应的字符集。

如果是Linux,字符集在/etc/sysconfig/i18n设置:

  1. LANG="zh_CN.GB2312" (指定当前操作系统的字符集)
  2. SUPPORTED="zh_CN.GB2312"(指定当前操作系统支持的字符集)
  3. SYSFONT="lat0-sun16"(指定当前操作系统的字体)

3. 客户端NLS_LANG参数:该参数用于向Oracle指示客户端操作系统的字符集。

有了以上3个基本概念之后,我来阐述一下Oracle字符集转换的基本原则:

  1. 设置客户端的NLS_LANG为客户端操作系统的字符集
  2. 如果数据库字符集等于NLS_LANG,数据库和客户端传输字符时不作任何转换
  3. 如果它们俩不等,则需要在不同字符集间转换,只有客户端操作系统字符集是数据库字符集子集的基础上才能正确转换,否则会出现乱码。

几种常见情况分析

下面先看一个例子,再透过现象看本质,我们会针对这个例子进行分析。

该例子如下:

  1. 1. 数据库字符集为Unicode(UTF-8编码)
  2. 我们的数据库版本是10.2.0.4.0,数据库字符集是:
  3. SQL> select * from v$nls_parameters where parameter='NLS_CHARACTERSET';
  4. PARAMETER                                VALUE
  5. ---------------------------------------- ------------------------------
  6. NLS_CHARACTERSET               AL32UTF8
  7. 2. 客户端操作系统字符集为代码页936(字符集为ZHS16GBK)
  8. 可以使用chcp获得windows的代码页(code page)
  9. C:\Documents and Settings\a105024\Desktop>chcp
  10. Active code page: 936
  11. 3. 创建测试表
  12. SQL> create table test(id number,var varchar2(30));
  13. Table created.
  14. 4. 插入数据
  15. 这里在同一个操作系统启动两个session,session1的NLS_LANG设为和数据库字符集一样(即AL32UTF8):
  16. C:\Documents and Settings\a105024\Desktop>set nls_lang=Simplified Chinese_China.AL32UTF8
  17. 连接数据库并插入一条数据:
  18. Session_1>insert into test values(1,'中国');
  19. 1 row created.
  20. Session_1>commit;
  21. Commit complete.
  22. session2的NLS_LANG设为和客户端操作系统一样(即ZHS16GBK):
  23. C:\Documents and Settings\a105024\Desktop>set nls_lang=Simplified Chinese_China.ZHS16GBK
  24. 连接数据库并插入一条数据:
  25. Session_2>insert into test values(2,'中国');
  26. 1 row created.
  27. Session_2>commit;
  28. Commit complete.
  29. 5. 执行查询
  30. 在session 1上执行查询:
  31. Session_1>select * from test;
  32. ID VAR
  33. ---------- ---------------------
  34. 1 中国
  35. 2 涓   浗
  36. 在session 2上执行查询:
  37. Session_2>select * from test;
  38. ID VAR
  39. ---------- --------------------
  40. 1 ???
  41. 2 中国

上面例子看起来很诡异,session1和2都能正常显示自己插入的字符串,又都不能正常显示对方插入的字符串。为了弄清楚,我们首先得知道数据库里对这两个字符串是怎么存储的。我们可以使用dump函数获得字符在数据库的编码:

  1. SQL> select id,dump(var,1016) from test;
  2. ID DUMP(VAR,1016)
  3. -- ------------------------------------------------------------
  4. 1 Typ=1 Len=4 CharacterSet=AL32UTF8: d6,d0,b9,fa
  5. 2 Typ=1 Len=6 CharacterSet=AL32UTF8: e4,b8,ad,e5,9b,bd

根据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操作就能轻易揭开它的假面具:

  1. Session_1>select length(var) from test where id=1;
  2. LENGTH(VAR)
  3. -----------
  4. 3

得出的长度居然为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,作者:朱显杰

Oracle 的字符集与乱码的更多相关文章

  1. oracle client字符集设置 乱码问题

    程序员经常要连接数据库   下面 就说一下 oracle数据库 客户端与服务器端 字符集一致性的问题   这可以解决中文乱码,其他字符乱码问题   主要是指在sqlplus中,其他类似toad/pls ...

  2. 更改Oracle字符集避免乱码

    如何更改Oracle字符集避免乱码 转一位大神的笔记. 国内最常用的Oracle字符集ZHS16GBK(GBK 16-bit Simplified Chinese)能够支持繁体中文,并且按照2个字符长 ...

  3. 如何更改Oracle字符集避免乱码

    转一位大神的笔记. 国内最常用的Oracle字符集ZHS16GBK(GBK 16-bit Simplified Chinese)能够支持繁体中文,并且按照2个字符长度存储一个汉字.UTF8字符集是多字 ...

  4. oracle的字符集设置与乱码

    oracle的字符集设置与乱码 字符集问题一直叫人头疼,究其原因还是不能完全明白其运作原理. 在整个运行环节中,字符集在3个环节中发挥作用: 1.软件在操作系统上运作时的对用户的显示,此时采用操作系统 ...

  5. 转:php连接oracle设定字符集,避免乱码

    原文来自于:http://muyu.iteye.com/blog/399884 数据库用oracle,当php连接oracle的时候,最好指定字符集. 查PHP手册,oci_connect的第四个参数 ...

  6. 【Oracle+PHP】php连接oracle设定字符集,避免乱码

    数据库用oracle,当php连接oracle的时候,最好指定字符集. 查PHP手册,oci_connect的第四个参数为charset,这是关键. 首先获取oracle的字符集,运行“select ...

  7. oracle数据库字符集的修改

    本文摘自:http://blog.csdn.net/nsj820/article/details/65711051.改客户端字符集:通过WINDOWS的运行菜单运行Regedit,修改注册表 Star ...

  8. 向Oracle数据库插入中文乱码解决方法

    解决方法:    第一步:sqlplus下执行:select userenv('language') from dual;//查看oracle字符集     注:如果oracle字符集与后台代码设置的 ...

  9. 选择ORACLE数据库字符集

    如何选择数据库的字符集是一个有争议的话题,字符集本身涉及的范围很广,它与应用程序.客户的本地环境.操作系统.服务器等关系很密切,因此要做出合适的 选择,需要明白这些因素之间的关系.另外对字符集的基本概 ...

随机推荐

  1. android 启动模式介绍

    Android启动模式 (1)Task:与Android系统是个多任务的系统中的任务是不同的.后者更倾向于多进程和多线程来说的,而这里的任务与application(应用程序)和activity(活动 ...

  2. 整理常用加密 iOS 与 Android 加密 MD5-SHA1

    1.MD5算法 不可逆 128位或者64位串,byte数字长度就是16和8,一般表示是使用16进制来表示的话,1个byte转换成2个16bit,分别表示高地位,所以生成的字符串是16位或者是32位的, ...

  3. 转:IE兼容模式下 SCRIPT1028: 缺少标识符、字符串或数字

    IE兼容模式下 SCRIPT1028: 缺少标识符.字符串或数字例如下面一段代码 var a = {    x: 1,    y: 2,};alert(a.x);如果在IE的兼容性视图(IE7文档模式 ...

  4. ubuntu kylin 14.04安装配置MongoDB v2.6.1(转)

    1.获取最新版本 https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.6.1.tgz 2.解压并进入bin目录 tar zxvf mongo ...

  5. 开发管理系统时,安装sqlserver2005问题整理

    最近在为单位开发一个综合管理系统.但是由于时间的问题,有时候就把程序带回家进行修改.但是家里有没有环境,就把数据库文件和程序带回家,可是随之问题来了.要重新在家里陪着开发环境,vs2008非常快的就安 ...

  6. 用字体在网页中画Icon图标

    第一步,下载.IcoMoon网站选择字体图标并下载,解压后将fonts文件夹放在工程目录下.fonts文件夹内有四种格式的字体文件: 注:由于浏览器对每种字体的支持程度不一致,要想在所有浏览器中都显示 ...

  7. NYOJ----776删除元素

    删除元素 时间限制:1000 ms  |  内存限制:65535 KB 描述 题意很简单,给一个长度为n的序列,问至少删除序列中多少个数,使得删除后的序列中的最大值<= 2*最小值 输入 多组测 ...

  8. 解决 node-gyp command not found 的问题

    node-gyp明明已经安装了,但是不能执行,显示命令找不到,然后重装之,发现npm有一个提示信息: npm WARN prefer global node-gyp@3.4.0 should be i ...

  9. 20145213《信息安全系统设计基础》实验一 Linux开发环境的配置

    北京电子科技学院(BESTI) 实 验 报 告 课程:信息安全系统设计基础 班级:1452 姓名: 黄亚奇 祁玮 学号:20145213 20145222 成绩: 指导教师:娄嘉鹏 实验日期:2016 ...

  10. 使用 Docker 编译 OpenWRT(Widora)

    Docker 是一种新的被称之为容器的虚拟机.本文将使用此工具,进行 OpenWRT 的编译. 在 Docker 中下载 Ubuntu 14.04 的镜像 使用以下命令可以十分方便的从远程服务器上将 ...