最后我们来讨论一下乱码的产生。
通常在我们的现实环境中,存在3个字符集设置。
第一: 客户端应用字符集(Client Application Character Set)
第二: 客户端NLS_LANG参数设置
第三: 服务器端,数据库字符集(Character Set)设置
我们说,一个字符在客户端应用(比如SQLPLUS,CMD,NOTEPAD等)中以怎样的字符显示取决于客户端操作系统,客户端能够显示怎样的字符,
我们就可以在应用中录入这些字符,至于这些字符能否在数据库中正常存储,就和另外的两个字符集设置紧密相关了。
在传输过程中,客户端NLS_LANG主要用于进行转换判断
如果NLS_LANG等于数据库字符集,则不进行任何转换直接把字符插入数据库
如果不同则进行转换,转换主要有两个任务
- 如果存在对应关系,则把相应二进制编码经过映射后(这一步映射以后,所代表的字符可能发生转换)传递给数据库
- 如果不存在对应关系,则传递一个替换字符(很多平台就是?)
数据库字符集,在和客户端NLS_LANG不同时,会把经过NLS_LANG转换的字符进行进一步处理
- 对于?(即不存在对应关系的字符)直接以?形式存放入数据库
- 对于其他字符,在NLS_LANG和数据库字符集之间进行转换后存入。
以下我们来看一下最为常见的字符集及乱码的产生:
1.当NLS_LANG字符集与数据库字符集不同,同时NLS_LANG不同于Server端字符集设置
在这种情况下,存在两种可能:
- 客户端输入的字符在NLS_LANG中没有对应的字符,这时无法转换,NLS_LANG使用替换字符替代这些无法映射的字符(这一步转换在TTS中
完成),在很多字符集中这个替代字符就是”?”
- 当客户端的字符在NLS_LANG中对应了不同的字符时,传递给数据库以后发生转换,存储的是字符,但是已经丢失了元数据,数据库中
的字符不再代表客户端的输入。而且这个过程不可逆,这也就是为什么很多时候在客户端输入的是正常的编码,查询之后会得到未知字符的原因。
我们通过上图来简单说明一下这个过程,当客户端在WE8ISO8859P15字符集时,输入欧元符号: €,这时客户端NLS_LANG和数据库端字符集不同,
进行第一次转换,客户端€符号编码是A4,在NLS_LANG转换时,A4对应了NLS_LANG中的‘¤’,这一步的转换产生了错误映射。由于数据库字符集不
同于NLS_LANG设置,这时进一步的转换发生了,存入数据库的编码变成了C2A4,虽然同NLS_LANG进行了正确的转换,但是客户端录入的数据已经
损坏或者丢失了。
我们可以用我们熟悉的字符集做一个简单的测试:
测试环境:
客户端应用为中文18030字符集
NLS_LANG设置为US7ASCII字符集
数据库CHARACTER SET为ZHS16GBK
c:\>set NLS_LANG=AMERICAN_AMERICA.US7ASCII
c:\>sqlplus eygle/eygle
SQL*Plus: Release 9.2.0.4.0 - Production on Tue Nov 4 01:19:57 2003
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
With the Partitioning, Oracle Label Security, OLAP and Oracle Data Mining options
JServer Release 9.2.0.4.0 - Production
SQL> insert into test values('测试');
1 row created.
SQL> select name,dump(name) from test;
NAME DUMP(NAME)
--------------------------------------------------
2bJT Typ=1 Len=4: 50,98,74,84
这时候我们发现,查询出来的是混乱的字符,我们把这些字符转换为2进制就是
110010 1100010 1001010 1010100
补全8位就是 00110010 01100010 01001010 01010100
我们把首位换成1 10110010 11100010 11001010 11010100
我们来看正确的存储: c:\>set nls_lang=AMERICAN_AMERICA.ZHS16GBK
c:\>sqlplus eygle/eygle
SQL*Plus: Release 9.2.0.4.0 - Production on Tue Nov 4 01:40:18 2003
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
Connected to: Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production With the Partitioning, Oracle Label Security, OLAP and Oracle Data Mining options JServer Release 9.2.0.4.0 - Production
SQL> insert into test values('测试');
1 row created.
SQL> col dump(name) for a30 SQL> select name,dump(name) from test;
NAME DUMP(NAME) ---------- ------------------------------ 测试 Typ=1 Len=4: 178,226,202,212
1 row selected.
我们把这个结果转换为2进制表示 10110010 11100010 11001010 11010100
这个结果正是我们前面乱码首位补全1后的结果。
这个测试说明在US7ASCII转换中文的时候除去了首位的 1,这样就丢失了元数据,导致乱码出现,NLS_LANG的转换作用由此可加一斑!
|
3. NLS_LANG和数据库字符集相同时
在这种情况下,数据库端对客户端传递过来的编码不进行任何转换(这样可以提高性能),直接存储进入数据库,那么这时候就存在和上面同样的问题,
如果客户端传递过来的字符集在数据库中有正确的对应就可以正确存储,如果没有,就会被替换字符置换成?,乱码就这样产生了。
如上图所示,当NLS_LANG和数据库字符集设置相同都为UTF8时,客户端的欧元符号的编码A4就不会经过任何转换就插入到数据库中,而在UTF8的数
据库中,A4代表的是一个非法字符。
我们来看一个简单的测试
测试环境:
客户端字符集应用为中文GB18030
客户端NLS_LANG为US7ASCII
数据库字符集为US7ASCII
我们知道这个时候,存入的数据,数据库不进行任何转换,在以下的测试中,我们看到中文在US7ASCII字符集下得以正确显示。
c:\>set nls_lang=AMERICAN_AMERICA.US7ASCII
c:\>sqlplus eygle/eygle
SQL*Plus: Release 9.2.0.4.0 - Production on Tue Nov 4 01:02:04 2003
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
With the Partitioning, Oracle Label Security, OLAP and Oracle Data Mining options
JServer Release 9.2.0.4.0 - Production
SQL> insert into test values('测试');
1 row created.
SQL> commit;
Commit complete.
SQL> select * from test;
NAME
----------
测试
1 row selected.
SQL> col dump(name) for a30
SQL> select name,dump(name) from test;
NAME DUMP(NAME)
---------- ------------------------------
测试 Typ=1 Len=4: 178,226,202,212
1 row selected.
SQL> select * from nls_database_parameters;
PARAMETER VALUE
------------------------------ ----------------------------------------
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_CHARACTERSET US7ASCII
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE AMERICAN
NLS_SORT BINARY
NLS_TIME_FORMAT HH.MI.SSXFF AM
PARAMETER VALUE
------------------------------ ----------------------------------------
NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY $
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
NLS_NCHAR_CHARACTERSET AL16UTF16
NLS_RDBMS_VERSION 9.2.0.4.0
20 rows selected.
SQL>
|
结语:
对于DBA来说,有一个很重要的原则就是:不要把你的数据库置于危险的境地!
这就要求我们,在进行任何可能对数据库结构发生改变的操作之前,先做有效的备份,很多DBA没有备份的操作中得到了惨痛的教训。
分享到:
相关推荐
关于GBK和Unicode字符集转换乱码问题
oracle数据库字符集 无法修改数据库字符集 乱码
从远程oracle数据库取数据是乱码,因为远程oracle字符集为AMERICAN_AMERICA.US7ASCII 本地oracle字符集为SIMPLIFIED CHINESE_CHINA.ZHS16GBK 所以是乱码,解决办法请下载文档 多谢 因为要下载其他资源无分 多多...
Oracle数据库字符集问题解析
oracle修改字符集,解决乱码问题.
Unicode字符集 8.0,2015-06-17最新标准
java字符集编码乱码详解
GB18030-2005 信息技术 中文编码字符集,单双四字节所有字符,包含简繁生僻字。
两种字符训练集格式,白字黑底和多种颜色的真实车牌,可根据需要获取使用,包含了字母和省份的训练集,均为个人辛苦整理,希望大家一起学习,不要为训练集苦恼
GB 18030-2005 信息技术 中文编码字符集
GB-T 2312-1980 信息交换用汉字编码字符集 基本集
linux 下的 oracle 导入数据库时出现中文乱码,通过修改字符集格式,修改成支持中文的格式。简单易操作。
文档中主要介绍了各类字符集以及相关的字符编码,字符的显示原理,从输入到显现的整个过程,程序中出现的乱码问题以及解决方案
解决两个问题:一、从反编译代码的界面CTRL+C复制是如果选中内容包含中文,贴到其他编辑器时,中文丢失; 二、打开xml文件、properties文件等包含中文时。中文在反编译界面中显示乱码。用其他工具打开正常。
字符集原理分析: 首先介绍一下目前我们系统中采用的不同字符集种类。第一,数据下载(中间件输出数据)。中间件输出数据的字符集都是GBK。GBK用在jsp(html)页面中,限定了输出字符集编码格式,输出到客户端的字节流...
GB 2312-1980 信息交换用汉字编码字符集 基本集
[原创]PWX for AS400 两段式字符集处理(杨晓东2005-12-08)
character-set-server/default-character-set:服务器字符集,默认情况下所采用的。 character-set-database:数据库字符集。 character-set-table:数据库表字符集。 优先级依次增加。所以一般情况下只需要设置...
修改RedHatEnterpriseLinux4.0操作系统字符集解决乱码问题。