ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

OCP笔记:五、全球化与字符集

2021-06-11 11:33:45  阅读:221  来源: 互联网

标签:编码 NLS 全球化 字符 数据库 FORMAT 字符集 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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有