2010-03-30 60 views
17

我做了下面的「模擬」:爲什麼新的String(bytes,enc).getBytes(enc)不返回原始字節數組?

byte[] b = new byte[256]; 

for (int i = 0; i < 256; i ++) { 
    b[i] = (byte) (i - 128); 
} 
byte[] transformed = new String(b, "cp1251").getBytes("cp1251"); 

for (int i = 0; i < b.length; i ++) { 
    if (b[i] != transformed[i]) { 
     System.out.println("Wrong : " + i); 
    } 
} 

對於cp1251這隻能輸出一個錯誤字節 - 在25
位置KOI8-R - 無一不精。
對於cp1252 - 4或5的差異。

這是什麼原因以及如何克服?

我知道這是錯誤在任何編碼中都將字節數組表示爲字符串,但這是支付提供商協議的要求,所以我沒有選擇。

更新:代表它ISO-8859-1作品,我會使用它的byte[]部分,cp1251的文字部分,因此,問題仍然只是出於好奇

回答

11

某些「字節」在目標集中不受支持 - 它們被替換爲?字符。當您轉換回來時,通常將?轉換爲字節值63 - 這不是以前的情況。

+0

太棒了。我實際上是在.NET中尋找答案,但他們在行爲上的相似程度足以讓我從中收集到。謝謝。 – 2010-03-30 12:33:44

4

看來,這兩個CP1251和cp1252的字節值不符合定義的字符;即它們是「不可映射的」。

String(byte[], String)的Javadoc這樣說:

此構造時給出的字節是不是在給定的charset有效的行爲是不確定的。當需要對解碼過程進行更多的控制時,應該使用CharsetDecoder類。

其他構造這樣說:

這個方法總是會替換錯誤輸入和不可映射的字符序列與此charset的默認替換字符串。

如果您在實踐中看到這種事情發生,則表明您要麼使用錯誤的字符集,要麼給您一些錯誤的數據。無論哪種方式,繼續下去似乎沒有問題,這可能不是一個好主意。

我一直在試圖弄清楚是否有方法讓CharsetDecoder「保留」無法映射的字符,除非您願意實現自定義的解碼器/編碼器對,否則我不認爲這是可能的。但我也得出結論,即使嘗試也沒有意義。 (理論上)錯誤地將那些不可映射的字符映射到真正的Unicode代碼點。如果你這樣做,你的應用程序將如何處理它們?

7

,這是什麼

究其原因,原因是字符編碼不是necesarily bijective並沒有充分的理由期待他們。並非所有字節或字節序列在所有編碼中都是合法的,並且通常將非法序列解碼爲某種佔位符字符,如'?'或U+FFFD,這當然在重新編碼時不會產生相同的字節。

此外,某些編碼可能會將一些合法的不同字節序列映射到相同的字符串。

3

實際上應該有一個區別:值爲24的字節被轉換爲值爲0xFFFDchar;這是「Unicode替換字符」,用於不可翻譯的字節。當轉換回來時,你會得到一個問號(值63)。

在CP1251中,代碼24表示「輸入結束」,不能成爲正確字符串的一部分,這就是爲什麼Java認爲它是「不可譯」的原因。

2

歷史原因:在古代字符編碼(EBCDIC,ASCII)中,前32個編碼有特殊的「控制」含義,它們可能不會映射到可讀的字符。例如:退格鍵,鈴聲,回車。較新的字符編碼標準通常會繼承這一點,並且它們不爲前32個位置中的每一個定義Unicode字符。 Java字符是Unicode。

相關問題