“修复”Java中的字符串编码

时间:2010-04-12 14:46:43

标签: java encoding

我使用UTF-8编码从String数组创建byte[] 但是,应该使用其他编码(Windows-1252)创建它。

有没有办法将此String转换回正确的编码?

我知道如果你可以访问原始的字节数组很容易,但是我的情况为时已晚,因为它是由一个封闭的源库提供的。

4 个答案:

答案 0 :(得分:24)

由于似乎对这是否可能存在一些疑惑,我想我需要提供一个广泛的例子。

该问题声称(初始)输入是byte[],其中包含Windows-1252个编码数据。我会称之为byte[] ib(对于“初始字节”)。

对于这个例子,我将选择德语单词“Bär”(意为熊)作为输入:

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,因为这三个字母(以及大多数其他字母)在这两种编码中处于相同位置。)

问题继续说明其他一些代码(超出我们的影响力)已经使用UTF-8编码将byte[]转换为字符串(我将其称为String { {1}}表示“输入字符串”)。 is唯一输入可用于实现我们的目标(如果String可用,那将是微不足道的):

is

这显然会产生错误的输出“B ”。

目标是使用 String is = new String(ib, "UTF-8"); System.out.println(is); 来生成ib(或正确解码byte[])。

现在有些人声称从is 获取UTF-8编码的字节将返回一个与初始数组具有相同值的数组:

is

但是返回两个字符byte[] utf8Again = is.getBytes("UTF-8"); B的UTF-8编码,并且在重新解释为Windows-1252时肯定会返回错误的结果:

这一行产生输出“B�”,这是完全错误的(如果初始数组包含非单词“Bür”,它也会产生相同的输出。)

所以在这种情况下你无法撤消操作,因为信息丢失了。

实际上是可以撤消此类错误编码的情况。当所有可能(或至少发生的)字节序列在该编码中有效时,它更有可能工作。由于UTF-8有几个字节序列只是无效值,因此 会出现问题。

答案 1 :(得分:10)

我尝试了这个并且由于某种原因它起作用了

修复编码问题的代码(它没有完美运行,我们很快就会看到):

 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范围内的所有字符看起来都没问题。
  4. 现在,当我们试图"反向"过程,这是发生的事情:

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

    注意 - 我原本以为这与你的问题有关(因为我自己也在做同样的事情,我认为我分享了我所学到的东西),但似乎我的问题略有不同。也许这会帮助别人。

答案 2 :(得分:-1)

你想做什么是不可能的。获得Java String后,有关字节数组的信息将丢失。你可能有运气做“手动转换”。创建所有windows-1252字符的列表及其到UTF-8的映射。然后迭代字符串中的所有字符,将它们转换为正确的编码。

修改 作为一名评论者说这不起作用。转换Windows-1252字节数组时,如果是UTF-8,则必然会遇到编码异常。 (请参阅herehere)。

答案 3 :(得分:-3)

您可以使用此tutorial

你需要的字符集应该在rt.jar中定义(根据this