字符集和字符编码

字符集(Character set):是一个系统支持的所有抽象字符的集合。字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。

  • ASCII字符集
  • ZHS16GB231280字符集
  • ZHS16GBK字符集

字符编码(Character Encoding):是一套法则,使用该法则能够对自然语言的字符的一个集合(如字母表或音节表)与其他的一个集合(如电脑编码)进行配对。即在符号集与数字系统之间建立对应关系。

  • ASCII
  • ZHS16GBK
  • ZHT16BIG5
  • ZHS32GB18030

ASCII字符集及编码

ASCII编码:将ASCII字符集转换为计算机可以接收的数字系统的数的规则。将7位(bits)表示一个字符,共128字符。

由于7位编码的字符集只能支持128个字符,为了表示更多的欧洲常用字符,对ASCII进行了扩展。ASCII扩展字符集使用了8位(bits)表示一个字符,共256字符。

GB2312字符集及编码

早期Oracle对应的字符集为ZHS16CGB231280,只收录了6763个汉字,没有繁体字、韩文、日文字符,可显示的字符很少。

新的Oracle中对应的字符集为ZHS32GB18030:

  • 与UTF-8相同,采用多字节编码,每个字可以由1个、2个或4个字节组成
  • 编码空间庞大,最多可定义161万个字符
  • 支持中国国内少数民族的文字,不需要动用造字区
  • 汉字收录范围包含繁体汉字以及日韩汉字

unicode万国码

unicode包含了几乎人类所有可用的字符,每年还在不断增加,可以看做是一种通用的字符集。

优势:

  • 将全世界所有的字符统一化,统一编码
  • 不再出现字符不兼容和字符转换的问题

编码方式:

  • UTF-32编码:固定使用4个字节来表示一个字符,存在空间利用效率问题
  • UTF-16编码:对相对常用的60000余个字符使用两个字节进行编码,其余的使用4字节
  • UTF-8编码:兼容ASCII编码。拉丁文、希腊文等使用2个字节;包括汉字在内的其他常用字符使用3个字节,剩下的极少使用的字符使用4个字节

字符集和乱码

对于本地客户端,Oracle使用NLS_LANG环境变量配置编码,例如:

  1. SIMPLIFIED CHINESE_CHINA.ZHS16GBK

格式:语言.字符编码

查看表中字段编码:

  1. -- 查看taaa字段的每个值的编码
  2. select dump(aaa, 1016) from t;

oracle客户端连接到服务端时,会将客户端使用的字符集告知给服务端。客户端发出SQL时,服务端会根据客户端字符集对结果做相应的转换后再返回客户端。

字符集不同时的查询结果

当客户端字符集和服务端字符集不同时,会有不同的查询结果。

示例:

查看操作系统字符集:

chcp

返回936,对应Oracle的ZHS16GBK

数据库服务端编码为:ZHS16GBK

向表中插入两条数据:

insert into table t values('国家');
insert into table t values('國家');  -- 插入一条繁体字数据

当客户端设置编码为:SIMPLIFIED CHINESE_CHINA.ZHS16GBK,此时查询该表数据,可以正常显示简体字和繁体字。

当客户端设置编码为:SIMPLIFIED CHINESE_CHINA.ZHS16GB231280(不包含繁体字的编码),此时查询该表数据,两条数据都变成了简体。

在sqlplus连接到oracle服务端时,oracle服务端就可以得知客户端的编码,会根据客户端的编码对结果集做相应转换。繁体的國家在ZHS16GB231280编码集中不存在,但是oracle发现这个字符可以映射到ZHS16GB231280的国家,所以会将结果转换成简体。

当客户端设置编码为:SIMPLIFIED CHINESE_CHINA.US7ASCII(只包含一些ASCII编码),此时查询该数表数据,显示的结果都变成了问号??

ASCII编码的字符量很少,当字符的编码值超出了ASCII编码范围时,就会显示为问号

当客户端设置编码为:SIMPLIFIED CHINESE_CHINA.AL32UTF8(比GBK编码集还大的字符集),此时查询该表数据,显示的结果都变成了不认识的字符。

在GBK中,字符编码是2个字节:87,f8(GBK中的字编码), bc,d2(GBK中字编码)、b9,fa(GBK中的字编码)。

但是到了AL32UTF8中,字符编码是4个字节,所以就成了:87,f8,bc,d2表示成了一个字符,所以就映射到了其他字符。

字符集不同时的数据插入

当客户端字符集和服务端字符集不同时,会影响插入的数据。

示例:

服务端的字符集为:ZHS16GBK

客户端先使用SIMPLIFIED CHINESE_CHINA.ZHS16GBK向数据库插入两条数据:

insert into t values('国家');
insert into t values('國家');

然后,客户端使用不同的字符集发送同一条insert语句:

insert into t values('国家');

当客户端字符集为SIMPLIFIED CHINESE_CHINA.US7ASCII时:

插入完成后,直接查询t表,所有数据都显示问号。

把客户端字符集改回SIMPLIFIED CHINESE_CHINA.ZHS16GBK后,开始以ZHS16GBK编码插入的数据可以正常显示,但是使用US7ASCII编码插入的数据依然显示问号。

因为国家两个字在US7ASCII编码下不存在,所以当时插入数据库的值就是问号,所以不管以什么编码查询也都是查出来的问号。

多语言环境的数据库字符集

在多语言环境下,最好使用集合最大的字符集(AL32UTF8字符集)。

结论

字符乱码:

  • 由于本地字符集编码不包含数据库中的字符编码,导致显示乱码
  • 本地与数据库字符集不兼容,导致数据插入时已经变成乱码

出现的场景:

  • 数据的查询
  • 数据的插入、修改
  • dblink
  • exp/imp
  • expdp/impdp