關於ORACLE和MYSQL中文字元亂碼的根源剖析
關於資料庫的字符集問題一直都是一個比較噁心的問題,如果不瞭解其實質可能一直
都搞不清楚這個問題的根源,只能出了問題去度娘,這裡我打算相容ORACLE和MYSQL對字符集
的處理來描述亂碼出現的情形和如何防止亂碼問題出現的可能,本文只用UTF-8和GBK為
例子進行描述,請大家先記住'去'這個字的UTF8和GBK編碼,因為整個文章將用'去'字為
例子進行講述
GBK UTF8 中文
C8A5 E58EBB 去
同時整篇文章資料庫DATABASE端的字符集始終為UTF8
一般來講我們所說的亂碼一般來自於非英文字元,如中文,因為英文是ASCII中進行了定義的
所有的編碼方式一致
首先我們從2個報錯的例子來進行描述
ORACLE:
NLS_LANG設定NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
終端設定為UTF8
SQL> create table testchar(name varchar2(20));
Table created.
SQL> insert into testchar values('去');
ERROR:
ORA-01756: quoted string not properly terminated
MYSQL:
character_set_client gbk
character_set_connection gbk
character_set_results gbk
終端設定為UTF8
mysql> create table testchar(name varchar(20));
Query OK, 0 rows affected (0.25 sec)
mysql> insert into testchar values('去');
ERROR 1406 (22001): Data too long for column 'name' at row 1
終端設定為UTF8
咋一看這兩個報錯不知所云啊,明明我給的varchar是20,ORACLE直接
報錯單引號沒有正確結束,MYSQL更狠直接報超長。
其實這就是字符集引起的錯誤。我會慢慢講述
首先解釋有關的字符集:
1、DATABASE字符集 資料庫字符集就是你期望的使用儲存資料的字符集,但是這裡說只是期望的,比如我的資料庫是
UTF8,是不是隻能儲存UTF8的字元呢?當然答案是否定的,他可以儲存任何字符集的字元,其實
字元儲存在檔案中就是一堆二進位制0和1,要看你的終端怎麼解釋他們了。比如如果檔案中儲存是
二進位制E58EBB,那麼你用UF8的終端字符集去解釋他就是'去'字,用GBK去解釋就是亂碼。
ORALCE中可以檢視nls_database_parameters 中的NLS_CHARACTERSET檢視資料庫的字符集
MYSQL中可以檢視character_set_database引數檢視資料庫的字符集
2、CLIENT轉換字符集 轉換字符集用於讓資料庫服務端知道客戶端過來的資料是什麼字符集,如果過來的不是資料庫的字符集
則進行轉換,當然轉換必須是相容的字符集,如果CLIENT使用的是GBK而DATABASE字符集是UTF8那麼進行
SQL解析的時候會進行轉為相應的字符集GKB->UT8,也就是說CLIENT字符集代表是你告訴資料庫你使用的什麼字符集
然後資料庫來決定是否需要轉換。
ORACLE中使用NLS_LANG來設定 如NLS_LANG=AMERICAN_AMERICA.AL32UTF8
MYSQL中使用set names進行設定他實際改變了三個引數character_set_client,character_set_connection
和character_set_results
3、終端字符集 這裡終端的字符集是你錄入資料和顯示資料的字符集,比如我用的securtCRT偽終端就是在
會話選項-->終端-->外觀-->字元編碼,當然如果不使用偽終端那麼LINUX中使用的locale進行
檢視終端的字元編碼,export LC_ALL=en_US.gbk 可以更改所有的,具體可以自己看一下。
當然windows也有
那麼瞭解了3種字符集我們來簡單描述一下:
如果我設定終端為GBK,CLIENT轉換字符集為GBK,DATABASE字符集為UTF8,那麼我們輸入一個'去'字,並且SELECT出來的流程為
終端以GBK字符集編碼錄入資料為C8A5--->
資料來到了CLIENT比如SQL命令視窗編碼為C8A5--->
回車後對SQL進行解析CLIENT的字符集為GBK和DATABASE UTF8不同進行轉換為E58EBB--->
資料進入DATAFILE 儲存格式為E58EBB--->
終端SQL命令視窗發起SELECT檢視要求插敘資料'去'--->
DATABASE查詢資料檔案讀取E58EBB返回給使用者SESSION--->
使用者SESSION發現CLIENT字符集為GBK進行轉為C8A5--->
終端顯示資料解析C8A5為GBK格式顯示為'去'
整個過程大概就是這樣,如果一個環節出現問題可能出現亂碼,比如轉換後為C8A5但是終端為UTF8格式
那麼必然亂碼
回到剛才的問題
mysql> insert into testchar values('去');
ERROR 1406 (22001): Data too long for column 'name' at row 1
SQL> insert into testchar values('去');
ERROR:
ORA-01756: quoted string not properly terminated
由於我的CLIENT字符集設定為GBK,而終端字符集設定為UTF8,資料庫字符集為UTF8
那麼我們錄入的資料編碼為E58EBB,然後進行解析由於CLIENT設定為GBK,DATABASE為UTF8,認為要
進行轉換,那麼叫E58EBB當做GBK進行轉為UTF8。當然就出現了問題因為E58EBB壓根不是GBK的編碼
轉出來肯定是亂碼,那麼我們如何糾正呢?
1、設定CLIENT字符集設定為UTF8
或者
2、設定終端字符集為GBK
都可以
接下來我們來驗證我們的說法。
一、在ORACLE和MYSQL中設定終端字符集為GBK,設定CLIENT字符集為UTF8,DATABASE字符集為UT8
那麼這種情況下我們插入'去'字,那麼終端是C8A5,而CLIENT字符集和DTABASE字符集相同
不會進行轉換,這樣就吧GBK的字元編碼資料存入了UTF8的字符集的庫,接下來演示
1、ORACLE
SQL> insert into testchar values('去');
1 row created.
SQL> select dump(name,16) from testchar;
DUMP(NAME,16)
--------------------------------------------------------------------------------
Typ=1 Len=2: c8,a5
我們看到我們的UTF8字符集的庫儲存進了C8A5為GBK字符集資料。
當然檢視也沒有問題因為我們的終端字符集為GBK,它會自動進行轉換
SQL> select * from testchar;
NAME
--------------------
去
如果我們設定終端字符集為UTF8那麼就出現了亂碼
SQL> r
1* select * from testchar
NAME
--------------------
?
2、MYSQL
mysql> show variables like '%char%';
+--------------------------+-----------------------------+
| Variable_name | Value |
+--------------------------+-----------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | /mysql/test/share/charsets/ |
+--------------------------+-----------------------------+
mysql> insert into testchar values('去');
Query OK, 1 row affected (0.00 sec)
直接檢視資料檔案的使用工具bcview(是我自己用C語言編寫的),來檢視實際的資料檔案
testchar.ibd提取出其資料。
可以在百度雲
下載到
[root@hadoop1 test]# bcview testchar.ibd 16 146 2;
******************************************************************
This Tool Is Uesed For Find The Data In Binary format(Hexadecimal)
Usage:./bcview file blocksize offset cnt-bytes!
file: Is Your File Will To Find Data!
blocksize: Is N kb Block.Eg: 8 Is 8 Kb Blocksize(Oracle)!
Eg: 16 Is 16 Kb Blocksize(Innodb)!
offset:Is Every Block Offset Your Want Start!
cnt-bytes:Is After Offset,How Bytes Your Want Gets!
Edtor QQ:22389860!
Used gcc version 4.1.2 20080704 (Red Hat 4.1.2-46)
******************************************************************
----Current file size is :0.093750 Mb
----Current use set blockszie is 16 Kb
current block:00000000--Offset:00146--cnt bytes:02--data is:0002
current block:00000001--Offset:00146--cnt bytes:02--data is:0000
current block:00000002--Offset:00146--cnt bytes:02--data is:ffff
current block:00000003--Offset:00146--cnt bytes:02--data is:c8a5
current block:00000004--Offset:00146--cnt bytes:02--data is:0000
current block:00000005--Offset:00146--cnt bytes:02--data is:0000
這裡塊00000003就是這個表的第一個資料塊關於如何使用BCVIEW和檢視
資料參考我的部落格
http://blog.itpub.net/7728585/viewspace-2071787/
可以看到資料為c8a5為GBK的字符集編碼。
如果這個時候我們修改終端字符集為UTF8,也會出現亂碼
mysql> select * from testchar;
+------+
| name |
+------+
| ? |
+------+
1 row in set (0.00 sec)
這樣一看ORACLE MYSQL都是一樣的沒有問題。亂碼的出現在於GBK編碼的字符集不能在
終端進行UTF8編碼的解析。
二、那麼如何將正確的UTF8 '去'字的編碼E58EBB存入到資料庫呢?其實前面已經給出了答案
將CLIENT字符集設定為GBK同時終端字符集設定為GBK
或者CLIENT字符集設定為UTF8同時終端設定為UTF8
我們使用複雜一點的將CLIENT字符集設定為GBK同時終端字符集設定為GBK
測試
1、ORACLE
設定終端字符集為GBK同時
[oradba@hadoop1 ~]$ export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
SQL> insert into testchar values('去');
1 row created.
SQL> commit;
Commit complete.
SQL> select dump(name,16) from testchar;
DUMP(NAME,16)
--------------------------------------------------------------------------------
Typ=1 Len=3: e5,8e,bb
可以看到e5,8e,bb為我們正確的UTF8字符集表面的'去'字
2、MYSQL
mysql> set names gbk;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like '%char%';
+--------------------------+-----------------------------+
| Variable_name | Value |
+--------------------------+-----------------------------+
| character_set_client | gbk |
| character_set_connection | gbk |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | gbk |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | /mysql/test/share/charsets/ |
+--------------------------+-----------------------------+
8 rows in set (0.01 sec)
[root@hadoop1 test]# bcview testchar.ibd 16 146 3;
current block:00000003--Offset:00146--cnt bytes:03--data is:e58ebb
可以看到e58ebb為UTF8的字符集編碼了。
SQL> select * from testchar;
NAME
----------------------------------------
?
這個時候亂碼是因為回顯的時候 utf8 服務端字符集編碼 e58ebb--> GBK CLIENT字符集轉換c8a5-->utf8 終端解析亂碼
那麼也許大家要問如何最大限度的避免亂碼的出現
方案為
1、設定終端字符集(或者確定.sql檔案)為UTF8、CLIENT字符集為UTF8、database字符集為UTF8。
這樣不存在轉換完全取決於你終端的輸入的字元的編碼,關於有時候我們要SOURCE SQL檔案,
那麼如果我們設定終端字符集為UTF8然後more一下這個檔案如果看到的中文沒有問題,如果沒有
問題那麼就能你能正常匯入資料。
2、按照需求設定比如你檔案或者終端是GBK編碼,那麼你必須設定CLIENT字符集為GBK,也就是說轉換
字符集修改為你終端輸入或者檔案本身的編碼。這樣才能正常的進行轉化不會出現亂碼。
同時在重點強調一下資料庫database的字符集為UTF8並不表示只能儲存UTF8的字元編碼,這個已經在前面的
例子驗證過。
最後測試一下WINDOWS 我們修改環境變數為
american_america.AL32UTF8
sqlplusw /nolog
終端字符集為
C:\Users\Administrator>chcp
活動內碼表: 936
936是GBK編碼
然後檢視本來的'去'字
SQL> connect sys/gelc123@192.168.190.30:1521/test as sysdba
Connected.
SQL> select * from testchar;
NAME
--------------------
鍘?
發現出現問題亂碼,為什麼呢?因為WINDOWS終端本生的字符集編碼為GBK,
而你去告訴ORACLE 不需要進行轉換,那麼正確的UTF8編碼e5,8e,bb被原封不動
的傳輸給終端,終端用GBK進行解析當然GBK解析e5,8e,bb肯定亂碼。
我們只需要修改轉換字符集為
american_america.ZHS16GBK
再次檢視
SQL> connect sys/gelc123@192.168.190.30:1521/test as sysdba
Connected.
SQL> select * from testchar;
NAME
----------------------------------------
去
沒有問題。
關於linux的locale參考如下:
Installing the locales package
In Debian, locales is a seperate package from glibc, so you'll have to install it in order to generate and use the Japanese locale in Liunx. During installation, debconf will ask you to choose which locales to generate. For starters, pick ja_JP.EUC-JP and en_US. You may also want to choose en_US.UTF-8 and ja_JP.UTF-8 for unicode support.
After installation, you can choose and generate locales manually by editing /etc/locale.gen. The contents of mine are as follows:
# This file lists locales that you wish to have built. You can find a list
# of valid supported locales at /usr/share/doc/locales/SUPPORTED.gz. Other
# combinations are possible, but may not be well tested. If you change
# this file, you need to rerun locale-gen.
en_US ISO-8859-1
en_US.UTF-8 UTF-8
ja_JP.EUC-JP EUC-JP
ja_JP.UTF-8 UTF-8
After editing the file, run locale-gen as root.
Setting up the environment
There are several special environment variables that determine how various locale-specific tasks should be handled.
LANG - Specifies the default locale for all unset locale variables
LANGUAGE - Most programs use this for the language of its interface
LINGUAS - (Obsolete?) The WindowMaker window manager used to use this instead of LANGUAGE.
LC_ALL - Overrides the locale for all LC_ variables and LANG
LC_CTYPE - Character handling functions
LC_COLLATE - Affects glob bracket ranges and sort order
LC_MESSAGES - Language that messages should be written in. Most programs use the value of LANGUAGE instead.
LC_NUMERIC - Determines the thousands seperator and how to write floating point numbers
LC_TIME - How to format dates and times
To run X with an English interface but the ability to display, input, and copy-paste Japanese text, set the environment variables as follows prior to running X or in your .Xsession file. These examples assume your are running a Bourne-like shell, such as sh, bash, or zsh:
export LANGUAGE=en
export LINGUAS=en
export LC_CTYPE=ja_JP # May break certain things like window maker; untested
export LC_TIME=C # Format time as English
export LC_NUMERIC=C # Format numbers as English
export LC_MESSAGES=C # Output messages in English
export LC_COLLATE=ja_JP # Do sorting and collating of characters as Japanese
export LANG=ja_JP # Use Japanese for all others
If you are using a c-shell like csh or tcsh, use the following instead:
setenv LANGUAGE en
setenv LINGUAS en
setenv LC_CTYPE ja_JP
setenv LC_TIME C
setenv LC_NUMERIC C
setenv LC_MESSAGES C
setenv LC_COLLATE ja_JP
setenv LANG ja_JP
至少我們可以得出結論LC_*覆蓋LC_ALL覆蓋LANG ,
所有我們需要設定
export LC_ALL=zh_CN.GBK
export LANG=zh_CN.GBK
但是在測試中這些設定並不影響vi的編輯和輸出,他影響的是LINUX自己的程式比如date
[root@ora12ctest ~]# date
2016年 08月 16日 星期二 02:27:36 CST
這個必須要設定客戶端字符集為gbk才行否則亂碼,如下:
[root@ora12ctest ~]# date
201686:28:27 CST
所以我們得出的結論不受locale影響。也就是客戶端使用什麼字符集vi錄入資料還是什麼字符集,顯示也是依據客戶端字符集
比如設定LC_*為GBK,客戶端使用UTF8那麼vi錄入資料還是UTF8,如果more這個檔案只要客戶端使用UTF8字符集就能正常
顯示
如:客戶端設定為UTF8
[root@ora12ctest ~]# locale
LANG=zh_CN.GBK
LC_CTYPE="zh_CN.GBK"
LC_NUMERIC="zh_CN.GBK"
LC_TIME="zh_CN.GBK"
LC_COLLATE="zh_CN.GBK"
LC_MONETARY="zh_CN.GBK"
LC_MESSAGES="zh_CN.GBK"
LC_PAPER="zh_CN.GBK"
LC_NAME="zh_CN.GBK"
LC_ADDRESS="zh_CN.GBK"
LC_TELEPHONE="zh_CN.GBK"
LC_MEASUREMENT="zh_CN.GBK"
LC_IDENTIFICATION="zh_CN.GBK"
LC_ALL=zh_CN.GBK
vi test3.txt檔案錄入
高鵬
[root@ora12ctest ~]# file test3.txt
test3.txt: UTF-8 Unicode text
[root@ora12ctest ~]# more test3.txt
高鵬
錄入和顯示均沒有問題。
下面轉自網路:
locale把按照所涉及到的文化傳統的各個方面分成12個大類,這12個大類分別是:
1、語言符號及其分類(LC_CTYPE)
2、數字(LC_NUMERIC)
3、比較和排序習慣(LC_COLLATE)
4、時間顯示格式(LC_TIME)
5、貨幣單位(LC_MONETARY)
6、資訊主要是提示資訊,錯誤資訊,狀態資訊,標題,標籤,按鈕和選單等(LC_MESSAGES)
7、姓名書寫方式(LC_NAME)
8、地址書寫方式(LC_ADDRESS)
9、電話號碼書寫方式(LC_TELEPHONE)
10、度量衡表達方式 (LC_MEASUREMENT)
11、預設紙張尺寸大小(LC_PAPER)
12、對locale自身包含資訊的概述(LC_IDENTIFICATION)。
所以說,locale就是某一個地域內的人們的語言習慣和文化傳統和生活習慣。一個地區的locale就是根據這幾大類的習慣定義的,這些locale定 義檔案放在/usr/share/i18n/locales目錄下面,例如en_US, zh_CN and de_DE@euro都是locale的定義檔案,這些檔案都是用文字格式書寫的,你可以用寫字板開啟,看看裡邊的內容,當然出了有限的註釋以外,大部分 東西可能你都看不懂,因為是用的Unicode的字元索引方式。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7728585/viewspace-2086271/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 關於Oracle字元亂碼?Oracle字元
- Oracle 中文字元及中文亂碼判斷Oracle字元
- 關於JS裡的字元表情亂碼JS字元
- mysql中文亂碼MySql
- 中文字元亂碼的解決字元
- oracle 中文亂碼Oracle
- 關於中文亂碼問題(總結)
- mysql中文亂碼和差8小時MySql
- 解決MySQL中文亂碼和插入中文不顯示的方法MySql
- Ubuntu 字元介面中文亂碼問題Ubuntu字元
- MySQL解決中文亂碼MySql
- Oracle學習系列—關於字元數字混合排序和中文排序的問題Oracle字元排序
- Oracle Sql Loader中文字元匯入亂碼的解決方案OracleSQL字元
- oracle字元亂碼問題的解決Oracle字元
- mysql字元轉化以及亂碼原因MySql字元
- oracle中文顯示亂碼Oracle
- 關於Oracle和MySQL中的無密碼登入OracleMySql密碼
- 關於javamail亂碼?JavaAI
- 關於字元編碼字元
- MySql中文亂碼問題解決MySql
- 解決MySQL中文亂碼問題MySql
- Java,MySQL中文亂碼問題求教JavaMySql
- ofbiz中mysql的中文亂碼問題!MySql
- Oracle11g字元亂碼解決Oracle字元
- Linux下關於解決JavaSwing中文亂碼的情況LinuxJava
- Linux下修改MySQL資料庫字元編碼為UTF-8解決中文亂碼LinuxMySql資料庫字元
- 徹底解決Oracle中文亂碼Oracle
- 關於mysql和jsp的中文問題~謝謝MySqlJS
- 關於jsp中的亂碼!!!!JS
- mysql 插入中文亂碼解決方案 轉MySql
- MySQL客戶端中文亂碼問題。MySql客戶端
- Navicat for mysql 顯示中文亂碼問題MySql
- 字元編碼再次剖析字元
- 關於mysql字元和數字型別轉換的問題研究MySql字元型別
- 關於【s】和[t]字元字元
- 關於Oracle字元型別的比較Oracle字元型別
- 解決 SecureCRT 和 SecureFX 中文亂碼Securecrt
- Oracle 10g 安裝中文亂碼Oracle 10g