将字符串从一个字符集转换为另一个



我正在将一个字符串从一个字符集转换为另一个字符集中,并阅读了许多关于它的示例,最终找到了下面的代码,这对我来说很好,作为字符集编码的新手,我想知道这是否是正确的方法。

public static byte[] transcodeField(byte[] source, Charset from, Charset to) {
return new String(source, from).getBytes(to);
} 

要将字符串从ASCII转换为EBCDIC,我必须执行以下操作:

System.out.println(new String(transcodeField(ebytes,
Charset.forName("US-ASCII"), Charset.forName("Cp1047"))));

要从EBCDIC转换为ASCII,我必须执行以下操作:

System.out.println(new String(transcodeField(ebytes,
Charset.forName("Cp1047"), Charset.forName("US-ASCII"))));

您找到的代码(transcodeField)不会将String从一种编码转换为另一种编码,因为String没有编码。它将字节从一种编码转换为另一种编码。只有当您的用例满足两个条件时,该方法才有用:

  1. 您的输入数据是一个编码中的字节
  2. 您的输出数据必须是其他编码中的字节

在这种情况下,它是直接的:

byte[] out = transcodeField(inbytes, Charset.forName(inEnc), Charset.forName(outEnc));

如果输入数据包含无法在输出编码中表示的字符(例如将复数UTF8转换为ASCII),则这些字符将被?替换符号替换,并且数据将被损坏。

然而,很多人问";如何将字符串从一种编码转换为另一种";,很多人用以下片段回答:

String s = new String(source.getBytes(inputEncoding), outputEncoding);

这完全是胡说八道。getBytes(String encoding)方法返回一个字节数组,其中包含以指定编码编码的字符(如果可能,无效字符将再次转换为?)。带有第二个参数的String构造函数从字节数组中创建一个新的String,其中字节采用指定的编码。现在,由于您刚刚使用source.getBytes(inputEncoding)来获取这些字节,所以它们在outputEncoding中不是编码的(除非编码使用相同的值,这对于像abcd这样的"正常"字符是常见的,但与更复杂的重音字符éêäöñ不同)。

那么这意味着什么呢?这意味着,当您拥有JavaString时,一切都很棒。Strings是unicode,这意味着您的所有字符都是安全的。当您需要将String转换为字节时,问题就来了,这意味着您需要决定编码。选择与unicode兼容的编码方式,如UTF8UTF16等是非常棒的。这意味着即使你的字符串包含各种奇怪的字符,你的字符仍然是安全的。如果您选择不同的编码(US-ASCII是最不支持的),您的字符串必须只包含编码支持的字符,否则将导致损坏的字节。

现在最后给出一些好用法和坏用法的例子。

String myString = "Feng shui in chinese is 風水";
byte[] bytes1 = myString.getBytes("UTF-8");  // Bytes correct
byte[] bytes2 = myString.getBytes("US-ASCII"); // Last 2 characters are now corrupted (converted to question marks)
String nordic = "Här är några merkkejä";
byte[] bytes3 = nordic.getBytes("UTF-8");  // Bytes correct, "weird" chars take 2 bytes each
byte[] bytes4 = nordic.getBytes("ISO-8859-1"); // Bytes correct, "weird" chars take 1 byte each
String broken = new String(nordic.getBytes("UTF-8"), "ISO-8859-1"); // Contains now "Här är några merkkejä"

最后一个例子表明,尽管这两种编码都支持北欧字符,但它们使用不同的字节来表示它们,并且在Mojibake中解码时使用了错误的编码。因此,不存在";将字符串从一种编码转换为另一种编码";,你永远不应该使用坏榜样。

还要注意,您应该始终指定使用的编码(同时使用getBytes()new String()),因为您不能相信默认编码总是您想要的。

最后一个问题是,Charset和Encoding不是一回事,但它们有很大的相关性。

从技术上讲,字符串在JVM中内部存储的方式是UTF-16编码,最高可达Java 8,变量编码从Java 9开始,但开发人员不需要关心这一点。


注意

有可能有一个损坏的字符串,并能够通过篡改编码来清除它,这可能是这个";将字符串转换为其他编码";误解源于。

// Input comes from network/file/other place and we have misconfigured the encoding 
String input = "Här är några merkkejä"; // UTF-8 bytes, interpreted wrongly as ISO-8859-1 compatible
byte[] bytes = input.getBytes("ISO-8859-1"); // Get each char as single byte
String asUtf8 = new String(bytes, "UTF-8"); // Recreate String as UTF-8

如果在input中没有字符被破坏,则该字符串现在将是"0";固定";。然而,正确的方法是在读取input时使用正确的编码,而不是事后修复。尤其是如果它有可能被破坏的话。

相关内容

最新更新