2010-04-12 7 views
17

Ich habe eine String erstellt von einer byte[] Array, UTF-8-Codierung verwenden.
Es sollte jedoch mit einer anderen Codierung (Windows-1252) erstellt werden."Fix" String-Codierung in Java

Gibt es eine Möglichkeit, diese Zeichenfolge zurück in die richtige Codierung zu konvertieren?

Ich weiß, es ist einfach zu tun, wenn Sie Zugang zum ursprünglichen Byte-Array haben, aber es ist in meinem Fall zu spät, weil es von einer geschlossenen Quellbibliothek gegeben ist.

Antwort

21

Da scheint es einige Verwirrung darüber, ob dies möglich ist oder nicht, ich denke, ich muss ein umfangreiches Beispiel geben.

Die Frage behauptet, dass die (erste) Eingabe eine byte[] ist, die Windows-1252 kodierte Daten enthält. Ich rufe das byte[]ib (für "anfängliche Bytes").

Für dieses Beispiel werde ich das deutsche Wort „Bär“ (was bedeutet, Bär) als Eingang wählen:

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. 

(Wenn Ihr JVM nicht, dass die Codierung nicht unterstützt, dann können Sie mit ISO-8859 -1 stattdessen, weil diese drei Buchstaben (und die meisten anderen) in diesen beiden Kodierungen an der gleichen Position sind.

Die Frage geht es weiter, dass einige andere Code (also außerhalb unseres Einflusses ist) bereits umgesetzt, dass byte[] in einen String mit der UTF-8-Codierung (ich nenne werde das Stringis für „input String“). Die String ist der nur Eingang die unser Ziel, zur Verfügung zu erreichen (wenn is vorhanden wären, wäre es trivial sein):

String is = new String(ib, "UTF-8"); 
System.out.println(is); 

Dies erzeugt offensichtlich den falschen Ausgang „B“.

Das Ziel wäre, ib (oder die korrekte Dekodierung dieser byte[]) zur Verfügung mit nuris zu produzieren.

nun einige Leute behaupten, dass das UTF-8-codiertes Bytes immer von diesen is wird einen Array mit den gleichen Werten wie die anfänglichen Array zurück:

byte[] utf8Again = is.getBytes("UTF-8"); 

Aber das gibt die UTF-8-Codierung von die beiden Zeichen B und und gibt definitiv das falsche Ergebnis, wenn neu interpretiert als Windows 1252:

System.out.println(new String(utf8Again, "Windows-1252"); 

Diese Linie erzeugt die Ausgabe „B "", was völlig falsch ist (es ist auch die gleiche Ausgabe, die das Ergebnis wäre, wenn das ursprüngliche Array stattdessen das Nicht-Wort "Bür" enthalten würde).

So in diesem Fall können Sie die Operation nicht rückgängig machen, weil Informationen verloren gehen.

Dort sind in der Tat Fälle, in denen solche Mis-Encodings rückgängig gemacht werden können. Es ist wahrscheinlicher, dass es funktioniert, wenn alle möglichen (oder zumindest vorkommenden) Bytefolgen in dieser Codierung gültig sind. Da UTF-8 mehrere Byte-Sequenzen hat, die einfach keine gültigen Werte sind, haben Sie Probleme.

+1

Ich bekomme das Problem jetzt. Es tut uns leid. Es ist wie ein Überlauf-Problem während der Codierung eines _invalid_ UTF-8-Bytes [], das Datenverlust verursacht. Danke für den Unterricht. – nicerobot

+1

Das ist das Unicode-Ersatzzeichen, das die drei Bytes 0xEF 0xBF 0xBD hat. –

-3

können Sie diese Sie tutorial

Die charset verwenden müssen in rt.jar definiert werden soll (nach this)

-1

Was möchten Sie ist unmöglich zu tun. Sobald Sie eine Java-Zeichenfolge haben, gehen die Informationen über das Byte-Array verloren. Möglicherweise haben Sie Glück, eine "manuelle Konvertierung" zu machen. Erstellen Sie eine Liste aller Windows-1252-Zeichen und deren Zuordnung zu UTF-8. Dann durchlaufen Sie alle Zeichen in der Zeichenfolge, um sie in die richtige Codierung zu konvertieren.

Bearbeiten: Wie ein Kommentator sagte, das wird nicht funktionieren. Wenn Sie ein Windows-1252-Byte-Array als UTF-8 konvertieren, sind Sie verpflichtet, Ausnahmen für die Codierung zu erhalten. (Siehe here und here).

+0

Das war, was ich fürchtete ... – Nico

8

habe ich versucht, dies und es funktionierte aus irgendeinem Grund

-Code-Codierung Problem zu reparieren (es ist nicht perfekt funktioniert, was wir sehen, in Kürze):

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); 

Die Ergebnisse sind:

input: …Und ich beweg mich (aber heut nur langsam) 
fixed: …Und ich beweg mich (aber heut nur langsam) 

Hier ist ein weiteres Beispiel:

input: Waun da wuan ned wa (feat. Wolfgang Kühn) 
fixed: Waun da wuan ned wa (feat. Wolfgang Kühn) 

Hier ist, was passiert ist und warum der Trick oben scheint zu funktionieren:

  1. Die Originaldatei war eine UTF-8 kodierten Textdatei (Komma getrennt)
  2. Diese Datei mit Excel aber der Benutzer versehentlich eingegeben importiert wurde Windows 1252 für die Kodierung (das war wahrscheinlich die Standardkodierung auf seinem Computer)
  3. Der Benutzer dachte, dass der Import erfolgreich war, weil alle Zeichen im ASCII-Bereich in Ordnung waren.

Nun, wenn wir den Prozess zu „reverse“ versuchen, hier ist was passiert:

// 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); 

Die Codierung oben Art von Arbeiten zur Festsetzung Code, sondern für die folgenden Zeichen nicht:

(Unter der Annahme, die nur Zeichen verwendet 1 Byte-Zeichen aus dem Windows 1252):

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 

Es funktioniert für einige der Charaktere, zBdiese:

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

Hinweis - ich ursprünglich dachte, dies auf Ihre Frage relevant war (und, wie ich auf die gleiche Sache arbeite ich selbst dachte, ich würde teilen, was ich gelernt habe), aber es scheint, mein Problem war etwas anders. Vielleicht hilft das jemand anderem.