1、多语言和字符集
多语言问题经过前人的提炼,简化为3个概念:语言Language、国家Nation、字符集。
- 语言:中文、英文、德问、日文等;
- 国家:支持同语言不同地域的书写习惯,如货币符号、日期、地址等。
- 字符集:包含字符个数和编码规则。如gb2312、gbk,前者支持的汉字少,后者支持的汉字多;
还有utf-8,同样的汉字对应的编码(编号)不一样。
一般前2个只设定了显示、消息提示的语言样式,第3个影响比较大。
CentOS设置语言和字符集:export lang=en_US.utf-8
Oracle设置语言和字符集:ZHS16GBK。
2、oracle字符集的含义
Oracle的字符集命名遵循以下命名规则:
即: <语言><比特位数><编码>
常见字符集含义
ZHS16GBK:表示采用GBK编码格式、16位(两个字节)简体中文字符集
WE8ISO8859P1:表示西欧、8位、ISO标准8859P1编码
AL32UTF8:表示其中AL代表ALL,指适用于所有语言
3、OS、oracle数据库、客户端3者字符集的关系
oracle数据库字符集:安装时设置,确定了数据的保存编码。
客户端:是客户端OS参数,定义与数据库交互的编码格式。
会话:默认来自客户端设置,可单独设置到会话级。会话创建时,会读取客户端的配置,作为会话的配置;也可单独设置会话字符集。
dmp文件:文件中包含了采用的字符集信息。
OS字符集:客户端拿到数据后的显示,是通过OS字符集的支持实现的。
4、字符集的转换
客户端(或者会话)字符集,设定了与Oracle数据库交互信息的编码规则,
如果与数据库的字符集不一致,会发生字符集的转换。
5、问题场景和原理分析
1、centOS的LANG参数这是为en_US.UTF-8,支持中文吗?
&:支持。
en_US指OS界面和提示消息使用英文和US的习惯,但文字符号范围和编码用的UTF-8,是包含中文的。
如果用的en_US,则系统自带的目录如文档、音乐等,用英文;如果用zh_CN,则中中文。
2、unicode与utf-8
&:unicode是国际化组织对人类全部语言符号的编码。当然包含了中文。
unicode的版本也是逐步升级的,版本越高,包含的语言符号越多。Unicode13.0中收入的汉字大约93000字左右。
unicode只是定制了语言符号的编码,具体如何在计算机中用字节表示,没有规定。
utf-8用变长字节表示unicode,如英文符号用1个字节表示,与ascii兼容;拉丁文、阿拉伯文,用2个字节;汉字用3个字节,
utf-8因其与ascii码兼容、节省字节,作为unicode的一种实现方式,成为了事实标准。
3、同一个Oracle数据库,不同客户端对中文支持不同?
&:数据库字符集是ZHS16GBK,确定数据的编码是GBK。
select * from v$nls_parameters;
NLS_CHARACTERSET ZHS16GBK
在windows的plsql developer中,可以支持中文。会话字符集是,
NLS_LANGUAGE SIMPLIFIED CHINESE
在linux的sqlplus中,不能支持中文,会话语言是:AMERICAN。
原理:从db输出到客户端拿到,都是gbk编码的中文,但linux客户端只支持英文,顾中文乱码了。
4、dmp从源库的GBK到目标库的utf8报错?
&:可以导入导出,且会自行进行变化转换,报错的原因是中文在不同编码占用的字节长度不同。
GBK中,一个汉字占用2个字节;utf8中,一个汉字占用3个字节。
一个字段长度是varchar2(10),在与源库,可存5个汉字;在目标库,只能存3个汉字,会超长。
可在目标数据库,增加字段长度解决。
5、单字节编码存储中文问题
&:由于历史的原因,早期的oracle没有中文字符集(如oracle6、oracle7、oracle7.1),但有的用户从那时起就使用数据库了,并用US7ASCII字符集存储了中文,或是有的用户在创建数据库时,不考虑清楚,随意选择一个默认的字符集,如WE8ISO8859P1或US7ASCII,而这两个字符集都没有汉字编码,虽然有些时候选用这种字符集好象也能正常使用。
正常情况下,要将汉字存入数据库,数据库字符集必须支持中文,而将数据库字符集设置为US7ASCII等单字节字符集是不合适的。US7ASCII字符集只定义了128个符号,并不支持汉字。另外,如果在SQL*PLUS中能够输入中文,操作系统缺省应该是支持中文的,但如果在NLS_LANG中的字符集设置为US7ASCII,显然也是不正确的,它没有反映客户端的实际情况。但在实际应用中汉字显示却是正确的,这主要是因为Oracle检查数据库与客户端的字符集设置是同样的,那么数据在客户与数据库之间的存取过程中将不发生任何转换,但是这实际上导致了数据库标识的字符集与实际存入的内容是不相符的。而在SELECT的过程中,Oracle同样检查发现数据库与客户端的字符集设置是相同的,所以它也将存入的内容原封不动地传送到客户端,而客户端操作系统识别出这是汉字编码所以能够正确显示。
在这个例子中,数据库与客户端都没有设置成中文字符集,但却能正常显示中文,从应用的角度看好象没问题。然而这里面却存在着极大的隐患,比如在应用length或substr等字符串函数时,就可能得到意外的结果。
对于早期使用US7ASCII字符集数据库的数据迁移到oracle8i/9i中(使用zhs16gbk),由于原始数据已经按照US7ASCII格式存储,对于这种情况,可以通过使用Oracle8i的导出工具,设置导出字符集为US7ASCII,导出后使用UltraEdit等工具打开dmp文件,修改第二、三字符,修改 0001 为0354,这样就可以将US7ASCII字符集的数据正确导入到ZHS16GBK的数据库中。
6、在导入过程中,最多会发生三次编码转换:
&:1、执行exp时,数据库中数据的编码会转换为导出客户端编码
2、执行imp时,dmp文件的编码转换为导入客户端编码
3、导入客户端编码转换为目标端数据库的数据库编码
参考
《UTF-8编码占几个字节?》 https://blog.csdn.net/bluetjs/article/details/52936943
《理解ORACLE数据库字符集》https://blog.csdn.net/sailing0123/article/details/4587733
《oracle ZHS16GBK的数据库导入到字符集为AL32UTF8的数据库(转载+自己经验总结)》 https://www.cnblogs.com/grisa/p/10077819.html