2015-01-09 76 views
1

UTF8轉換JDBC似乎當被問及從包含未定義LATIN1代碼頁字符LATIN1列讀取插入UTF8替換字符。這種行爲與MySQL的內部函數不同。怪誕LATIN1在JDBC

字符編碼是我一直停留在過去一週兔子洞,並在不產生100個明顯答案的利益,我將演示最新一對夫婦的代碼示例發生。

MySQL的:

[[email protected] ~]$ echo 'SELECT CONVERT(UNHEX("81") using latin1);' | mysql --init-command='set names latin1' | tail -1| hexdump -C 
00000000 81 0a            |..| 
00000002 
[[email protected] ~]$ echo 'SELECT CONVERT(UNHEX("81") using latin1);' | mysql --init-command='set names utf8' | tail -1| hexdump -C 
00000000 c2 81 0a           |...| 
00000003 

這是很明顯的,並完全按預期工作。 0x81是一個未定義的latin1代碼點。它以UTF8中的\u0081或以十六進制「在磁盤上」表示爲c2 81。現在

古怪來自JDBC,拿這個常規例子:

@GrabConfig(systemClassLoader=true) 
@Grab(group='mysql', module='mysql-connector-java', version='5.1.6') 
import groovy.sql.Sql 
sql = Sql.newInstance('jdbc:mysql://localhost/test', 'root', '', 'com.mysql.jdbc.Driver') 
sql.eachRow('SELECT CONVERT(UNHEX("C281") using utf8) as a;') { println "$it.a --" } 

這個查詢的輸出是兩個字節,c2 81預期。它很容易理解這裏發生了什麼。 Mysql連接默認爲UTF8。 unhexxed列也轉換爲UTF8(無編碼,因爲源是二進制的,CONVERT()之後的數據仍然是c2 81)。

現在考慮這種情況。連接仍然是UTF8,就像JDBC默認的一樣。我們將0x81字節轉換爲latin1,所以希望mysql能夠像上面bash例子中那樣將它轉換爲c2 81

@GrabConfig(systemClassLoader=true) 
@Grab(group='mysql', module='mysql-connector-java', version='5.1.6') 
import groovy.sql.Sql 
sql = Sql.newInstance('jdbc:mysql://localhost/test', 'root', '', 'com.mysql.jdbc.Driver') 
sql.eachRow('SELECT CONVERT(UNHEX("81") using latin1) as a;') { println "$it.a --" } 

運行此與groovy latin1_test.groovy | hexdump -C產生這樣的:

00000000 ef bf bd 0a          |....| 
00000004 

ef bf bd是UTF8替換字符。在utf8轉換失敗時使用的字符。

回答

2

JDBC似乎當被問及從包含LATIN1列讀取未定義latin1的代碼頁字符

是的,這是CharsetDecoder instances默認行爲,在默認情況下,when the (byte) input is malformed,將執行插入一個UTF8替換字符這個無法映射的字節序列的substitutionUnicode's replacement character, U+FFFD

使用此行爲的方法的示例全部爲Reader s,但String構造函數將字節數組作爲參數。這就是爲什麼您不應該使用String來存儲二進制數據的原因!

唯一的解決辦法,使一個錯誤就是抓住原始字節輸入,創建你自己的解碼器,並告訴它在這種情況下fail ...

+0

爲什麼Java的執行字符集轉換,當MySQL可以做它與集名稱utf8 ?.我遺憾地沒有訪問這裏的Java代碼(groovy只是爲了測試),因爲問題來自Sqoop,一個mysql到hadoop導入應用程序 –

+1

嗯,你要求打印內容,因此它必須做解碼,對吧? – fge

+0

數據是否可以用二進制打印(或者是什麼使它不被修改/轉換)。它只是,c2 81是一個有效的UTF8代碼點,並且mysql將任何latin1代碼點(00到FF)轉換爲U + 0000到U + 00FF,這可能是錯誤的,但至少它的可逆 –