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

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

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

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

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

该问题声称(初始)输入是包含Windows-1252编码数据的byte[] 。 我将调用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(我将该String称为“输入字符串”)。 String唯一可用于实现我们目标的输入 (如果可用,那将是微不足道的):

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

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

目标是 is可用的情况下产生ib (或正确解码该byte[] )。

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

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

但是,它返回两个字符B 的UTF-8编码,并在重新解释为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) 

这是正在发生的事情以及为什么上面的技巧似乎有效:

  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 ç 

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

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

编辑:作为评论者说这不会奏效。 转换Windows-1252字节数组时,如果是UTF-8,则必然会遇到编码exception。 (见这里和这里 )。

您可以使用本教程

你需要的charset应该在rt.jar中定义(根据这个 )