2012-02-16 98 views
12

我將要完成將數據庫從Latin1轉換爲UTF-8的繁瑣和棘手的任務。如何檢測Latin1編碼列中的UTF-8字符 - MySQL

在這一點上,我只是想檢查我的表中存儲了哪些數據,因爲這將決定我應該使用什麼方法來轉換數據。

具體來說,我想檢查在Latin1列中是否有UTF-8字符,那麼最好的方法是什麼?如果只有幾行受到影響,那麼我可以手動修復此問題。

選項1.執行MySQL轉儲並使用Perl搜索UTF-8字符?

選項2.使用MySQL CHAR_LENGTH查找具有多字節字符的行? 例如SELECT name FROM clients WHERE LENGTH(name) != CHAR_LENGTH(name); 這夠了嗎?

此刻我已將我的Mysql客戶端編碼切換爲UTF-8。

+0

根據定義,您不能在Latin1列中存儲UTF-8數據。謹慎地爲您的問題提供更多的背景信息? – deceze 2012-02-16 02:07:45

+0

UTF-8多字節字符都大於128.但實際上有**無法**來確定字符的意圖是什麼:我認爲你想要問的是「我可以檢測到非ASCII字符一個Latin1編碼列「。顯然,由於字節序列0xF0 0x53意味着UTF-8和拉丁語1中的兩個不同的東西,所以即使找到它,也不知道它是哪一個... – Borealid 2012-02-16 02:09:12

+3

@deceze您可能無意中將UTF-8數據存儲在LATIN1中列,因爲LATIN1是一個8位字符集。它只是看起來像一個混亂的編碼錯誤。 – tadman 2012-02-16 03:38:25

回答

37

與時區一樣,字符編碼是問題的常見來源。

您可以做的是查找任何「高-HASCII」字符,因爲它們是LATIN1重音字符或符號,或者是UTF-8多字節字符中的第一個。除非你欺騙一點,否則說出這種區別並不容易。

爲了弄清楚什麼編碼是正確的,你只需要SELECT兩個不同的版本,並在視覺上進行比較。這裏有一個例子:

SELECT CONVERT(CONVERT(name USING BINARY) USING latin1) AS latin1, 
     CONVERT(CONVERT(name USING BINARY) USING utf8) AS utf8 
FROM users 
WHERE CONVERT(name USING BINARY) RLIKE CONCAT('[', UNHEX('80'), '-', UNHEX('FF'), ']') 

這是由異常複雜,因爲MySQL的正則表達式引擎似乎忽略的東西像\x80並使得有必要使用UNHEX()方法來代替。

這產生的結果是這樣的:

latin1    utf8 
---------------------------------------- 
Björn    Björn 
+0

對遲到的迴應和模糊的初始問題抱歉。獲得這個答案是因爲它或多或少幫助我檢測可能意圖是UTF8字符的字符。 Upvoted deceze的答案,因爲它包含我在數據庫中其他地方的情況 – dinie 2012-02-23 02:07:34

+0

真棒 - 這個小塊幫助我解決了utf8編碼數據插入到utf8表時被解釋爲latin1的問題,因爲我通過mysql CLI輸入了它。雖然有趣,但因爲系統設置爲UTF8,所以在輸入和選擇時看起來很好(只是在關聯的網站上解碼和呈現時不會)。 – Kasapo 2012-10-25 14:36:09

+1

有時候,如果您從兩個連接讀取和寫入數據時,完全相同的錯誤配置,它會奇蹟般地工作。有時候,兩次錯誤確實是對的。 – tadman 2012-10-25 16:56:50

0

我將在數據庫和grep的轉儲所有有效UTF8序列。從哪裏拿到它取決於你得到什麼。關於識別無效的UTF8有很多問題。你基本上可以逆轉邏輯。

編輯:基本上,任何由7位ASCII組成的字段都是安全的,任何包含無效UTF-8序列的字段都可以假定爲Latin-1。剩下的數據應該被檢查 - 如果你幸運的話,少數幾個明顯的替換將會解決絕大多數問題(用Latin-1代替ö等)。

+1

[This answer](http://stackoverflow.com/a/7302465/333340)包含一個相當長的可能的壞組合列表。 – Synchro 2013-04-05 07:03:42

8

因爲你的問題不是完全清楚,讓我們假設一些情景:

  1. 迄今爲止連接錯誤:你一直連接到數據庫使用latin1編碼不正確,但存儲UTF-8數據在數據庫中(列的編碼在這種情況下是不相關的)。這是我描述的情況here。在這種情況下,很容易修復:通過latin1連接將數據庫內容轉儲到文件。這會將錯誤地存儲的數據轉換爲錯誤地正確存儲的UTF-8,這是迄今爲止它的工作方式(請閱讀上述關於血腥細節的文章)。然後,您可以通過正確設置的utf8連接將數據重新導入數據庫,並將其存儲爲應有的數據。
  2. 迄今爲止錯誤的列編碼:通過utf8連接將UTF-8數據插入到latin1列中。在那種情況下忘記它,數據就消失了。任何非latin1字符都應該替換爲?
  3. 迄今爲止一切都很好,此後增加了對UTF-8的支持:您已將Latin-1數據正確存儲在latin1列中,並通過latin1連接插入,但希望將其擴展爲也允許UTF-8數據。在這種情況下,只需將列編碼更改爲utf8。 MySQL將爲您轉換現有的數據。然後,只要確保在插入UTF-8數據時將數據庫連接設置爲utf8。
+0

如果多個客戶端一直在添加數據,並且其中一些客戶端認爲他們應該提交utf8,那麼您將會得到一個邪惡的混合,而這個混淆基本上需要手動進行。這並不意味着你不能自動完成部分流程,實際上大多數情況下都可以在沒有人爲干預的情況下決定。 – tripleee 2012-02-16 06:10:38

+0

的確如此,但是你真的完全是foobar。在嘗試回答這種情況之前,OP將需要提供更多有關實際問題的信息。 – deceze 2012-02-16 06:13:09

+0

對於情況1,對我來說,這個命令是'mysqldump --default-character-set = latin1 -u user -p database'。然後我必須進入轉儲文件並將'SET NAMES latin1'改爲'utf8'。然後重新導入轉儲文件和所有固定的。 – James 2017-02-16 23:02:39

3

a script on github來幫助這種事情。

+1

這個腳本對我來說工作得非常好,我做了一些改進,使它更快更靈活。我也有[分支](https://github.com/Synchro/mysql-convert-latin1-to-utf8/tree/utf8mb4),它轉換爲MySQL ['proper'utf8mb4 charset](http:// mathiasbynens.be/notes/mysql-utf8mb4)。 – Synchro 2013-04-05 07:03:26

+0

這個腳本的工作..仍然不明白它是如何工作的..需要經過它..從拉丁1 - utf8幾乎無痛地移動,不得不添加'mysql_set_charset(「utf8」);'爲PHP使用它後立即那。 – 2014-09-07 14:36:11