2010-04-12 35 views
17

我有一個String創建從一個byte[]數組,使用UTF-8編碼。
但是,它應該使用其他編碼(Windows-1252)創建。「修復」在Java中的字符串編碼

有沒有辦法將此字符串轉換回正確的編碼?

我知道如果你有權訪問原始字節數組很容易,但是我的情況已經太晚了,因爲它是由一個封閉的源庫提供的。

回答

21

由於似乎有一些混淆這是否可能,我想我需要提供一個廣泛的例子。

該問題聲稱(初始)輸入是包含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編碼字符串(我會打電話給那個Stringis爲「輸入字符串」)。這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有幾個字節序列,這些字節序列不是有效值,所以有問題。

+1

現在我得到了這個問題。抱歉。這就像編碼導致數據丟失的_invalid_ UTF-8字節[]的溢出問題。感謝您的教訓。 – nicerobot 2010-04-12 18:37:18

+1

是具有三個字節0xEF 0xBF 0xBD的Unicode替換字符。 – 2016-11-14 12:02:25

-3

您可以使用此tutorial

你需要應該在rt.jar中定義的字符集(根據this

-1

你想要做什麼是不可能的。一旦有了Java String,關於字節數組的信息就會丟失。你可能有幸做了一個「手動轉換」。創建一個所有的Windows-1252字符列表及其映射到UTF-8。然後遍歷字符串中的所有字符以將它們轉換爲正確的編碼。

編輯: 作爲評論者說這不起作用。當你轉換一個Windows-1252字節數組時,如果它是UTF-8的話,你肯定會得到編碼異常。 (見herehere)。

+0

這就是我害怕的...... – Nico 2010-04-12 15:05:18

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) 

這裏發生了什麼,爲什麼上面的伎倆似乎工作:

  1. 原始文件是UTF-8編碼的文本文件(逗號分隔)
  2. 該文件是進口與Excel但用戶誤輸入用於編碼的Windows 1252(這可能是他或她的計算機上的默認編碼)
  3. 用戶認爲導入是成功的,因爲ASCII範圍中的所有字符都看起來不錯。

現在,當我們試圖「扭轉」的過程中,會出現以下情況:

// 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   ç 

注 - 我本來以爲這是有關您的問題(和我的工作我自己我想我會分享我所學到的同樣的事情),但似乎我的問題是稍微不一樣。也許這會幫助別人。

相關問題