OCP笔记:五、全球化与字符集
作者:互联网
1. 字符集、字符编码、输入法
关于字符集、字符编码的问题,其实我一直比较困惑,一知半解,脑子里一团浆糊,这次借助学习Oracle字符集,理一下思路。
字符这种类型的数据和其它类型的数据如音频、视频等等,本质上,都是存储在内存或磁盘上的二进制数据,关键是程序如何解释。
这个解释字符数据的协议,就是字符集或说字符编码了。
为了理解,需要先理清定义一些概念:
字符:生活中语言的抽象符号,如“a”,“β”,“我”,“ひ”等,字符是对人类而言的抽象概念。
字体:计算机上有一个字体的概念,是字符的不同图像风格,计算机软件会给每个字符编号,然后通过这个编号搜索图像库中对应的图像,用于显示这个字符。
字符与字体的区别是:字体是具体的计算机图像,一个字符可能有多个字体。
字符集:给一个语言的所有字符编号,这些字符和编号的对应关系,称为字符集,字符集的典型例子是UNICODE,GB2312,GBK。
字符编码:对于UNICODE这种字符集,包含了全世界所有语言的所有字符,有的字符用一个字节编号,有的字符用两个或三个字节编号,读取的时候,怎么知道从连续的数据中提取几个字节,解释为一个字符呢?为解决这个问题,就发明了UTF8字符编码,UTF8就像一列火车,8bit就是一节车厢,根据一个UNICODE字符所用的字节数,用一节或六节车厢装载它,如:
UTF-8是一种变长字节编码方式。对于某一个字符的UTF-8编码,如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的位数,其余各字节均以10开头。UTF-8最多可用到6个字节。
如表:
1字节 0xxxxxxx
2字节 110xxxxx 10xxxxxx
3字节 1110xxxx 10xxxxxx 10xxxxxx
4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
对于GB2312和GBK(GBK是GB2312的扩展),字符集就是字符编码,它们每个字符固定用两个字节编号,每个字符的字符编码,就是它的编号。
还有ASCII、ANSI(ANSI是ASCII的扩展),字符集就是字符编码,它们每个字符用一个字节编号,
GB2312、GBK和UNICODE都是兼容ASCII的。
UTF8这列装载字符集的火车,也可以装其它字符集的数据,但是UTF8其实是专门为装载UNICODE字符集发明的。
任何软件(如操作系统、文本编辑器、sqlplus,xshell等),只要是需要处理或显示字符,就一定有一个字符编码属性,表示它所使用的字符编码。
当使用输入法输入中文或其它语言字符时,输入法只负责按照人的输入,找到那个字符,而接受输入的软件中,这些字符所使用的字符编码,取决于软件的字符编设置。输入时输入法应该已经与接收输入的软件协商过了,约定使用哪种字符编码,我猜测必然有这个过程,这一个过程用户并不了解。
例如,打开notepad++文本编辑器,设置的编码为GB2312,用输入法输入‘中‘,保存文件为gb2312.txt,
再设置编码为UTF8,用同样的输入法输入‘中’,保存文件为utf8.txt
用hexdump查看,两个文件保存的字节值是不一样的,因为它们使用的编码不同:
当软件打开文本文件时,需事先知道文本的字符编码,这个信息可能包含在文本文件中,如UTF8的BOM文件头,或软件事先设定,以正确的字符编码解读文本是第一步,如果需要将文本在屏幕显示出来,计算机会根据字符编码,到字体库找到对应的字体图像,在软件的窗口中画出来。
2. Oracle对全球化的支持
1)Oracle在创建数据库时,指定了一些数据库级别的全球化参数,这些参数决定了默认情况下,数据库以何种语言返回提示和错误信息、日期和时间的格式,字符串排序的标准。下图是所有数据库级别的全球化参数:
SYS@CDB> select * from nls_database_parameters;
PARAMETER VALUE
---------------------------------------- ----------------------------------------
NLS_RDBMS_VERSION 19.0.0.0.0
NLS_NCHAR_CONV_EXCP FALSE
NLS_LENGTH_SEMANTICS BYTE
NLS_COMP BINARY
NLS_DUAL_CURRENCY $
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZR
NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_FORMAT HH.MI.SSXFF AM
NLS_SORT BINARY
NLS_DATE_LANGUAGE AMERICAN
NLS_DATE_FORMAT DD-MON-RR
NLS_CALENDAR GREGORIAN
NLS_NUMERIC_CHARACTERS .,
NLS_NCHAR_CHARACTERSET AL16UTF16
NLS_CHARACTERSET AL32UTF8
NLS_ISO_CURRENCY AMERICA
NLS_CURRENCY $
NLS_TERRITORY AMERICA
NLS_LANGUAGE AMERICAN
其中 NLS_CHARACTERSET 和 NLS_NCHAR_CHARACTERSET 确立了数据库存储char、varchar2、nchar、nvarchar2的二进制格式(字符编码),数据库创建以后就无法更改(其实是可以更改,不过新的字符编码必须是旧字符编码的父集,如果数据库已经有数据,这个更改就算是一种迁移了):
这些NLS,分为数据库级别,实例级别,会话级别和语句级别,它们之间是互相重叠的,会话里取谁的值,由优先级决定:数据库 < 实例 < 会话 < 语句
查看实例级别的NLS参数:
SYS@CDB> select * from nls_instance_parameters;
PARAMETER VALUE
---------------------------------------- ----------------------------------------
NLS_LANGUAGE ITALIAN
NLS_TERRITORY AMERICA
NLS_SORT
NLS_DATE_LANGUAGE
NLS_DATE_FORMAT
NLS_CURRENCY
NLS_NUMERIC_CHARACTERS
NLS_ISO_CURRENCY
NLS_CALENDAR
NLS_TIME_FORMAT
NLS_TIMESTAMP_FORMAT
NLS_TIME_TZ_FORMAT
NLS_TIMESTAMP_TZ_FORMAT
NLS_DUAL_CURRENCY
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
在实际中,我们经常会修改会话或语句级别的NLS参数,如:
alter session set NLS_LANGUAGE="SIMPLIFIED CHINESE";
alter session set NLS_TERRITORY="JAPAN";
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
查看会话级别的NLS参数:
SYS@CDB> select * from nls_session_parameters;
PARAMETER VALUE
---------------------------------------- ----------------------------------------
NLS_LANGUAGE SIMPLIFIED CHINESE
NLS_TERRITORY CHINA
NLS_CURRENCY ¥
NLS_ISO_CURRENCY CHINA
NLS_NUMERIC_CHARACTERS .,
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE SIMPLIFIED CHINESE
NLS_SORT BINARY
NLS_TIME_FORMAT HH.MI.SSXFF AM
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参数,包括了数据库、实例、会话级别对NLS参数的设置,会话级别没设置的就看实例,实例没设置的按照数据库,设置了就按优先级覆盖,
SYS@CDB> select * from v$nls_parameters;
PARAMETER VALUE CON_ID
---------------------------------------- ---------------------------------------- ----------
NLS_LANGUAGE SIMPLIFIED CHINESE 3
NLS_TERRITORY CHINA 3
NLS_CURRENCY ¥ 3
NLS_ISO_CURRENCY CHINA 3
NLS_NUMERIC_CHARACTERS ., 3
NLS_CALENDAR GREGORIAN 3
NLS_DATE_FORMAT DD-MON-RR 3
NLS_DATE_LANGUAGE SIMPLIFIED CHINESE 3
NLS_CHARACTERSET AL32UTF8 3
NLS_SORT BINARY 3
NLS_TIME_FORMAT HH.MI.SSXFF AM 3
NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI.SSXFF AM 3
NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM TZR 3
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZR 3
NLS_DUAL_CURRENCY ¥ 3
NLS_NCHAR_CHARACTERSET AL16UTF16 3
NLS_COMP BINARY 3
NLS_LENGTH_SEMANTICS BYTE 3
NLS_NCHAR_CONV_EXCP FALSE 3
3. 客户端的字符编码与服务器端的字符编码
Oracle的客户端sqlpus也有字符编码属性,由客户端环境变量NLS_LANG控制,NLS_LANG除了包含字符编码信息,还包含语言和地域信息,如绿色部分表示语言,紫色部分表示地域,红色部分表示字符编码:
export NLS_LANG=AMERICAN_AMERICA.AL32UTF8
export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
export NLS_LANG="SIMPLIFIED CHINESE"_CHINA.ZHS16GBK
export NLS_LANG="SIMPLIFIED CHINESE"_CHINA.AL32UTF8
语言和地域信息,决定了显示提示信息和错误信息使用的语言,地域信息决定了日期的格式,不过最重要的还是字符编码信息,它决定了Oracle服务端,是否将传来的字节按照不同的字符集做映射转换。
需要注意:
当我使用终端软件如xshell连接到Linux服务器,登录到sqlplus的命令行,准备输入命令时,输入法是和xshell交互,不是和sqlplus交互。
如果xshell设置为GBK,那么xshell发给sqlplus的字符串s,都是以GBK编码的,无论sqlplus的编码设为何。
如果NLS_LANG为ZHS16GBK,就把s作为GBK来解析,如果如果NLS_LANG为AL32UTF8,就把s作为UTF8来解析。
当sqlplus与Oracle数据库的字符编码相同时:sqlplus传给服务端的SQL,直接被服务器端解析存储,没有字符集映射转换的过程。
当sqlplus与Oracle数据库的字符编码不同时:服务器收到SQL后会根据两种字符编码的字符集映射,做转换,也就是,
假如sqlplus的编码是GBK,Oracle数据库的编码是UTF8,sqlplus传给数据库一个 以GBK编码的'中' 字,值为D6D0,
数据库收到D6D0后,根据客户端编码为GBK服务端编码为UTF8这个信息,将D6D0转为’中‘在Unicode-UTF8中的编码E4B8AD,然后再分析或存储。
下面用sqlplus做一个实验:
将xshell的编码设为GBK,sqlplus设为export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK,数据库字符编码为UTF8,连接数据库执行:
可以看到,尽管输入的’中广‘两个字是用GBK编码的,值为D6D0B9E3,但存储到数据库里的RAW数据,是’中广‘两个字的Unicode-UTF8编码,
只有一个可能,就是对同一个字符,Oracle数据库在GBK字符集和Unicode字符集做了映射转换。
不过,为了减少出复杂问题的可能,我们是要求,终端(xshell)、sqlplus、Oracle数据库三者的字符编码是一样的。
在Oracle中不能单独设置表或字段的字符集/字符编码,所有char、varchar2、clob都使用NLS_CHARACTERSET,所有nchar、nvarchar2、nclob都使用NLS_NCHAR_CHARACTERSET。
软件间需要交换文本数据时,例如数据库之间的迁移,要注意文本字段的字符编码是否相同,数据库是否自动地做了同一个字符不同字符集之间的映射。不同数据库,文件类型之间的统一界面,就是看它们的字符集是否相同,字符编码是否相同,如果都相同,就可以从一个数据库读出二进制数据,直接写入另一个数据库,如果不同,就需要转换。
字符编码查看与转换的网址:
http://mytju.com/classcode/tools/encode_gb2312.asp
https://www.qqxiuzi.cn/bianma/zifuji.php
gb2323 -- gbk -- gb18030 :https://www.zhihu.com/question/19677619
标签:编码,NLS,全球化,字符,数据库,FORMAT,字符集,OCP 来源: https://blog.csdn.net/howard_shooter/article/details/117689944