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

Java 将字符串从一个字符集转换为另一个字符集,java,character-encoding,ascii,ebcdic,Java,Character Encoding,Ascii,Ebcdic,我正在致力于将字符串从一个字符集转换为另一个字符集,阅读了很多关于它的示例,最后找到了下面的代码,这对我来说很不错,作为一个字符集编码的新手,我想知道,这是否是正确的方法 public static byte[] transcodeField(byte[] source, Charset from, Charset to) { return new String(source, from).getBytes(to); } 要将字符串从ASCII转换为EBCDIC,我必须执行以下操作:

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

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"))));
System.out.println(new String(transcodeField(ebytes,
                Charset.forName("Cp1047"), Charset.forName("US-ASCII"))));
要将EBCDIC转换为ASCII,我必须执行以下操作:

System.out.println(new String(transcodeField(ebytes,
                Charset.forName("US-ASCII"), Charset.forName("Cp1047"))));
System.out.println(new String(transcodeField(ebytes,
                Charset.forName("Cp1047"), Charset.forName("US-ASCII"))));
您找到的代码(
transcodeField
)没有将
字符串
从一种编码转换为另一种编码,因为
字符串
没有编码。它将字节从一种编码转换为另一种编码。仅当您的用例满足2个条件时,该方法才有用:

  • 您的输入数据是一种编码中的字节
  • 您的输出数据需要是其他编码中的字节
  • 在这种情况下,这是直截了当的:

    byte[] out = transcodeField(inbytes, Charset.forName(inEnc), Charset.forName(outEnc));
    
    如果输入数据包含无法在输出编码中表示的字符(例如将复杂的
    UTF8
    转换为
    ASCII
    ),这些字符将被替换为
    ,数据将被损坏

    但是,请询问“”,并用以下代码片段回答:

    String s=新字符串(source.getBytes(inputEncoding),outputEncoding)

    这完全是胡说八道。
    getBytes(String encoding)
    方法返回一个字节数组,其中包含按指定编码编码的字符(如果可能,无效字符也会转换为
    )。带有第2个参数的字符串构造函数从字节数组创建新字符串,其中字节采用指定的编码。现在,由于您刚刚使用
    source.getBytes(inputEncoding)
    来获取这些字节,因此它们不是
    outputEncoding
    中的编码(除非编码使用相同的值,这对于像
    abcd
    这样的“普通”字符很常见,但与像重音字符这样的更复杂字符不同

    那么这意味着什么呢?这意味着当你有一个Java
    字符串时,一切都很好<代码>字符串
    是unicode,这意味着所有字符都是安全的。当您需要将
    字符串
    转换为字节时,就会出现问题,这意味着您需要决定编码。选择与unicode兼容的编码,如
    UTF8
    UTF16
    等,这非常好。这意味着您的字符仍然是安全的,即使您的字符串包含各种奇怪的字符。如果选择不同的编码(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ä"
    
    最后一个示例演示,尽管两种编码都支持北欧字符,但它们使用不同的字节来表示它们,并且在解码时使用错误的编码会导致错误。因此,不存在“将字符串从一种编码转换为另一种编码”这样的事情,并且您永远不应该使用断开的示例

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

    作为最后一个问题,它们不是一回事,但它们有很大的关联

    从技术上讲,字符串在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
    

    如果
    输入
    中没有损坏的字符,字符串现在将被“修复”。然而,正确的方法是在读取
    输入时使用正确的编码,而不是事后修复。尤其是当它有可能被破坏时。

    您是否运行了代码?它是否按预期工作?请使用此信息编辑您的问题。我需要将EBCDIC(HP)转换为ASCII,因此我得到的结果不符合预期。您是否尝试过我的解决方案?此问题很重要,因为它要求验证广泛共享的算法。然而,为了防止人们认为它是正确的,请提出问题,以明确它是错误的,并考虑接受@ KayaMn。迂回地,JavaLang.string仅为UTF 16,所以只能将字符串从UTF 16转换为UTF 16。正如您所指出的,其他转换可以是与字节数组之间的转换,也可以是与字节数组之间的转换。字符串是UTF-16代码单元的计数序列这一事实在Java中索引和迭代(以及执行字符算术)时非常重要。这就是为什么有很多方法处理Unicode代码点的原因。你是对的,我甚至没有涉及到代码点:)这个问题非常复杂,我想提供一个合理的答案,这样我就可以将它与所有“字符串转换”问题联系起来。如果你有任何改进的建议,一定要告诉我。我将尝试改进这个答案,并将其缩小一点。我唯一的批评是关于UTF-16的一点。我认为这是一个重要问题的好答案。这个问题很重要,因为它提出了一些非常错误的问题。