【Oracle字符集】乱码的产生原理

编辑手记:很多人在数据库各种操作中遭遇过乱码的问题,今天我们分析一下乱码产生的原因。本文节选于《循序渐进Oracle》

通常在现实环境中,存在3个字符集设置:

  • 客户端应用字符集(Client ApplicationCharacter Set);
  • 客户端NLS_LANG参数设置;
  • 服务器端,数据库字符集(Character Set)设置。

由于一个字符在客户端应用(如SQLPLUS、CMD、NOTEPAD等)中以怎样的字符显示取决于客户端操作系统,客户端能够显示怎样的字符,我们就可以在应用中录入这些字符。至于这些字符能否在数据库中正常存储,就和另外的两个字符集设置紧密相关了(通常我们可以忽略应用程序的字符集,这个字符集在应用程序安装时,已经被内在的决定,并且会依据操作系统的相关设置进行选择)。

在传输过程中,客户端NLS_LANG主要用于进行转换判断。如果NLS_LANG等于数据库字符集,则不进行任何转换直接把字符插入数据库;如果不同则进行转换,转换主要有两个任务:

  • 如果存在对应关系,则把相应二进制编码经过映射后(这一步映射以后,所代表的字符可能发生转换)传递给数据库。
  • 如果不存在对应关系,则传递一个替换字符(不同平台的替换字符各不相同,最常见的替换字符是“?”)。

数据库字符集,在和客户端NLS_LANG不同时,会对经过NLS_LANG转换的字符进一步处理:对于?(即不存在对应关系的字符)直接以?形式存放入数据库,对于其他字符,在NLS_LANG和数据库字符集之间进行转换后存入。

下面来看一下最为常见的字符集及乱码的产生。

NLS_LANG字符集与数据库字符集不同

当NLS_LANG字符集与数据库字符集不同,且NLS_LANG不同于客户端字符集设置时,存在以下两种可能。

1、客户端输入的字符在NLS_LANG中没有对应的字符,这时无法转换,NLS_LANG使用替换字符替代这些无法映射的字符(这一步转换在TTS中完成),在很多字符集中这个替代字符就是“?”。

2、当客户端的字符在NLS_LANG中对应了不同的字符时,传递给数据库以后发生转换,存储的是字符,但是已经丢失了元数据,数据库中的字符不再代表客户端的输入。而且这个过程不可逆,这也就是为什么很多时候在客户端输入的是正常的编码,而查询之后会得到未知字符的原因。

下面通过下图来简单说明一下这个过程。

当客户端在WE8ISO8859P15字符集时,输入欧元符号€,这时客户端NLS_LANG和数据库端字符集不同,进行第一次转换,客户端€符号编码是A4,在NLS_LANG转换时,A4对应了NLS_LANG中的“¤”,这一步的转换产生了错误映射。由于数据库字符集不同于NLS_LANG设置,这时进一步的转换发生了,存入数据库的编码变成了C2A4,虽然同NLS_LANG进行了正确的转换,但是客户端录入的数据已经损坏或者丢失了。

可以用我们熟悉的字符集做一个简单的测试(测试环境是客户端代码点对应中文18030字符集,NLS_LANG设置为US7ASCII字符集,数据库CHARACTER SET为ZHS16GBK)。

这时发现,查询出来的是混乱的字符,把这些字符转换为二进制就是:

110010 1100010 1001010 1010100

补全8位就是如下序列:

00110010 01100010 01001010 01010100

我们把首位换成1,得到如下序列:

10110010 11100010 11001010 11010100

接下来看正确的存储格式:

把这个结果转换为二进制表示:

10110010 11100010 11001010 11010100

这个结果正是前面乱码首位补全1后的结果。这个测试说明在US7ASCII转换中文的时候除去了首位的1,这样就丢失了元数据,导致乱码出现,NLS_LANG的转换作用由此可加一斑!

NLS_LANG和数据库字符集相同时

在这种情况下,数据库端对客户端传递过来的编码不进行任何转换(这样可以提高性能),直接存储进入数据库,那么这时候就存在和上面同样的问题,如果客户端传递过来的字符集在数据库中有正确的对应就可以正确存储,如果没有,就会被替换字符置换成,乱码就这样产生了。

如下图所示,当NLS_LANG和数据库字符集设置相同都为UTF8时,客户端的欧元符号的编码A4就不会经过任何转换就插入到数据库中,而在UTF8的数据库中,A4代表的是一个非法字符。

来看一个简单的测试(测试环境是客户端代码点对应中文18030字符集,客户端NLS_LANG为US7ASCII,数据库字符集为US7ASCII)。

我们知道这个时候存入的数据,数据库不进行任何转换,在以下的测试中,看到中文在US7ASCII字符集下得以正确显示。

原文发布于微信公众号 - 数据和云(OraNews)

原文发表时间:2017-01-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java帮帮-微信公众号-技术文章全总结

Oracle应用实战八(完结)——存储过程、函数+对象曹组

游标 在写java程序中有结果集的概念,那么在pl/sql中也会用到多条记录,这时候我们就要用到游标,游标可以存储查询返回的多条数据。 游标可以理解为是PL/S...

3436
来自专栏后端技术探索

mysql5.7强势支持原生json格式!!全面掌握

mysql一直是如此优秀,但是随着最近一些nosql的强劲发展,甚为关系型数据库的mysql,也不例外在某些层面稍有逊色。其中,是否支持json格式是最常被用来...

812
来自专栏企鹅号快讯

浅谈数据库Join的实现原理

Join的实现算法有三种,分别是Nested Loops Join, Merge Join, Hash Join。 DB2、SQL Server和Oracle都...

30510
来自专栏崔庆才的专栏

Python操作MySQL存储,这些你都会了吗?

2855
来自专栏前端儿

在PHP中使用MySQL Mysqli操作数据库 ,以及类操作方法

先来操作函数部分,普遍的MySQL 函数方法,但随着PHP5的发展,有些函数使用的要求加重了,有些则将废弃不用,有些则参数必填...

2453
来自专栏加米谷大数据

Hive的数据类型

本文介绍hive的数据类型,数据模型以及文件存储格式。这些知识大家可以类比关系数据库的相关知识。

1112
来自专栏Python研发

pymysql

pymsql是python中操作的MYsql的模块,其使用方法和MySQLdb几乎相同

1164
来自专栏V站

Python的flask:models.py来创建mysql数据库

3446
来自专栏禹都一只猫博客

Python的flask:models.py来创建mysql数据库

1836
来自专栏张善友的专栏

使用 SQL Server 2008 数据类型-xml 字段类型参数进行数据的批量选取或删除数据

我们经常有这样的需求,批量的删除或者选取大量的数据,有非常多的Id值,经常使用in条件查询,如果你使用拼接字符串的方式,可能遭遇SQL语句的长度限制4000个字...

2439

扫码关注云+社区