我有一個String
創建從一個byte[]
數組,使用UTF-8編碼。
但是,它應該使用其他編碼(Windows-1252)創建。「修復」在Java中的字符串編碼
有沒有辦法將此字符串轉換回正確的編碼?
我知道如果你有權訪問原始字節數組很容易,但是我的情況已經太晚了,因爲它是由一個封閉的源庫提供的。
我有一個String
創建從一個byte[]
數組,使用UTF-8編碼。
但是,它應該使用其他編碼(Windows-1252)創建。「修復」在Java中的字符串編碼
有沒有辦法將此字符串轉換回正確的編碼?
我知道如果你有權訪問原始字節數組很容易,但是我的情況已經太晚了,因爲它是由一個封閉的源庫提供的。
由於似乎有一些混淆這是否可能,我想我需要提供一個廣泛的例子。
該問題聲稱(初始)輸入是包含Windows-1252編碼數據的byte[]
。我會打電話給byte[]
ib
(對於「初始字節」)。
在這個例子中,我會選擇德語單詞「BAR」(意爲熊)作爲輸入:
byte[] ib = new byte[] { (byte) 0x42, (byte) 0xE4, (byte) 0x72 };
String correctString = new String(ib, "Windows-1252");
assert correctString.charAt(1) == '\u00E4'; //verify that the character was correctly decoded.
(如果您的JVM不支持該編碼,那麼你可以使用ISO-8859 -1,因爲這三個字母(和大多數其他字母)在這兩個編碼中處於相同的位置)。
問題繼續說一些其他的代碼(即我們的影響之外)已經完成轉換的byte[]
使用UTF-8編碼字符串(我會打電話給那個String
is
爲「輸入字符串」)。這String
是只輸入可用來實現我們的目標(如果是可用is
,這將是微不足道的):
String is = new String(ib, "UTF-8");
System.out.println(is);
這顯然會產生不正確的輸出「B」。
這樣做的目的是產生ib
(或byte[]
的正確解碼)與僅is
可用。
現在有些人聲稱,從is
得到UTF-8編碼的字節將用相同的值返回一個數組作爲初始陣列:
byte[] utf8Again = is.getBytes("UTF-8");
但是返回的UTF-8編碼兩個字符B
和�
,肯定會返回錯誤的結果時,重新解釋爲Windows的1252:
System.out.println(new String(utf8Again, "Windows-1252");
此行產生輸出「B ½「,這是完全錯誤的(如果初始數組包含非單詞」Bür「,結果也是相同的輸出)。
所以在這種情況下您不能撤消操作,因爲信息丟失。
還有是事實上,這種錯誤編碼可以被撤消。當所有可能的(或至少發生的)字節序列在該編碼中有效時,它更可能工作。由於UTF-8有幾個字節序列,這些字節序列不是有效值,所以將有問題。
我想這和它的工作由於某種原因
代碼修復編碼問題(它不能很好地工作,我們很快就會看到):
final Charset fromCharset = Charset.forName("windows-1252");
final Charset toCharset = Charset.forName("UTF-8");
String fixed = new String(input.getBytes(fromCharset), toCharset);
System.out.println(input);
System.out.println(fixed);
的結果是:
input: …Und ich beweg mich (aber heut nur langsam)
fixed: …Und ich beweg mich (aber heut nur langsam)
再舉一例:
input: Waun da wuan ned wa (feat. Wolfgang Kühn)
fixed: Waun da wuan ned wa (feat. Wolfgang Kühn)
這裏發生了什麼,爲什麼上面的伎倆似乎工作:
現在,當我們試圖「扭轉」的過程中,會出現以下情況:
// we start with this garbage, two characters we don't want!
String input = "ü";
final Charset cp1252 = Charset.forName("windows-1252");
final Charset utf8 = Charset.forName("UTF-8");
// lets convert it to bytes in windows-1252:
// this gives you 2 bytes: c3 bc
// "Ã" ==> c3
// "¼" ==> bc
bytes[] windows1252Bytes = input.getBytes(cp1252);
// but in utf-8, c3 bc is "ü"
String fixed = new String(windows1252Bytes, utf8);
System.out.println(input);
System.out.println(fixed);
編碼固定上述這類作品的代碼,但以下字符失敗:
(假設僅使用來自Windows 1252的1個字節字符的字符):
char utf-8 bytes | string decoded as cp1252 --> as cp1252 bytes
」 e2 80 9d | â€� e2 80 3f
Á c3 81 | Ã� c3 3f
Í c3 8d | Ã� c3 3f
Ï c3 8f | Ã� c3 3f
Рc3 90 | � c3 3f
Ý c3 9d | Ã� c3 3f
它確實適用於某些字符,例如這些:
Þ c3 9e | Þ c3 9e Þ
ß c3 9f | ß c3 9f ß
à c3 a0 | Ã c3 a0 à
á c3 a1 | á c3 a1 á
â c3 a2 | â c3 a2 â
ã c3 a3 | ã c3 a3 ã
ä c3 a4 | ä c3 a4 ä
å c3 a5 | Ã¥ c3 a5 å
æ c3 a6 | æ c3 a6 æ
ç c3 a7 | ç c3 a7 ç
注 - 我本來以爲這是有關您的問題(和我的工作我自己我想我會分享我所學到的同樣的事情),但似乎我的問題是稍微不一樣。也許這會幫助別人。
現在我得到了這個問題。抱歉。這就像編碼導致數據丟失的_invalid_ UTF-8字節[]的溢出問題。感謝您的教訓。 – nicerobot 2010-04-12 18:37:18
是具有三個字節0xEF 0xBF 0xBD的Unicode替換字符。 – 2016-11-14 12:02:25