【问题标题】:Java Strings Character Encoding - For French - Dutch LocalesJava 字符串字符编码 - 适用于法语 - 荷兰语语言环境
【发布时间】:2013-03-19 13:13:30
【问题描述】:

我有以下代码

public static void main(String[] args) throws UnsupportedEncodingException {
        System.out.println(Charset.defaultCharset().toString());

        String accentedE = "é";

        String utf8 = new String(accentedE.getBytes("utf-8"), Charset.forName("UTF-8"));
        System.out.println(utf8);
        utf8 = new String(accentedE.getBytes(), Charset.forName("UTF-8"));
        System.out.println(utf8);
        utf8 = new String(accentedE.getBytes("utf-8"));
        System.out.println(utf8);
        utf8 = new String(accentedE.getBytes());
        System.out.println(utf8);
}

上面的输出如下

windows-1252
é
?
é
é

有人可以帮我理解这是做什么的吗?为什么会有这个输出?

【问题讨论】:

  • 要获得预期的输出,请确保您为文件设置了文件编码类型“UTF8”。如果您使用的是 eclipse 右键单击​​文件选择属性并选择 utf8 作为文本文件编码类型。

标签: java character-encoding


【解决方案1】:

如果您已经有String,则无需立即对其进行编码和解码,该字符串已经是某人解码原始字节的结果。

在字符串文字的情况下,某人是编译器,将您的源代码作为原始字节读取并以您指定的编码对其进行解码。如果您已经以 Windows-1252 编码物理保存了源文件,并且编译器将其解码为 Windows-1252,那么一切都很好。如果没有,您需要通过声明编译器在编译源代码时使用的正确编码来解决此问题...

线

String utf8 = new String(accentedE.getBytes("utf-8"), Charset.forName("UTF-8"));

什么都不做。 (编码为 UTF-8,解码为 UTF-8 == no-op)

线

utf8 = new String(accentedE.getBytes(), Charset.forName("UTF-8"));

将字符串编码为 Windows-1252,然后将其解码为 UTF-8。结果只能在 Windows-1252 中解码(因为 它是在 Windows-1252 中编码的,呵呵),否则你会得到奇怪的结果。

线

utf8 = new String(accentedE.getBytes("utf-8"));

将字符串编码为 UTF-8,然后将其解码为 Windows-1252。与前一种情况相同的原则适用。

线

utf8 = new String(accentedE.getBytes());

什么都不做。 (编码为 Windows-1252,解码为 Windows-1252 == no-op)

用整数类比可能更容易理解:

int a = 555;
//The case of encoding as X and decoding right back as X
a = Integer.parseInt(String.valueOf(a), 10);
//a is still 555

int b = 555;
//The case of encoding as X and decoding right back as Y
b = Integer.parseInt(String.valueOf(b), 15);
//b is now 1205 I.E. strange result

这两个都没有用,因为在执行任何代码之前我们已经有了我们需要的东西,整数555

需要 当你的字符串离开你的系统时将你的字符串编码成原始字节,并且当它们进入你的系统时需要将原始字节解码成一个字符串。 系统内无需立即编码和解码。

【讨论】:

  • “(编码为 Windows-1252,解码为 Windows-1252 == no-op)”——不正确。它将破坏所有在 Windows 1252 中不可用的字符并将它们变成问号。
  • @KarolS 你把这句话断章取义了,听起来很聪明,哈哈
【解决方案2】:

第 1 行 - 您系统上的默认字符集是 windows-1252。

第 2 行 - 您通过将字符串文字编码为 UTF-8 字节创建了一个字符串,然后使用 UTF-8 方案对其进行解码。结果是格式正确的String,使用windows-1252编码可以正确输出。

第 3 行 - 您通过将字符串文字编码为 windows-1252 创建了一个字符串,然后使用 UTF-8 对其进行解码。 UTF-8 解码器检测到一个不可能是 UTF-8 的序列,并用问号“?”替换了有问题的字符。 (UTF-8 格式表示,任何最高位设置为 1 的字节都是多字节字符的一个字节。但是 windows-1252 编码只有一个字节长......所以,这是糟糕的 UTF- 8)

第 4 行 - 您通过 UTF-8 编码然后在 windows-1252 中解码创建了一个字符串。在这种情况下,解码并没有“失败”,但它产生了垃圾(又名 mojibake)。你得到 2 个字符的输出的原因是“é”的 UTF-8 编码是一个 2 字节序列。

第 5 行 - 您通过编码为 windows-1252 并解码为 windows-1252 创建了一个字符串。这会产生正确的输出。


总的教训是,如果您使用一种字符编码将字符编码为字节,然后使用不同的字符编码进行解码,您很可能会受到一种或另一种形式的破坏。

【讨论】:

    【解决方案3】:

    当你调用 String getBytes 方法时:

    使用平台的默认字符集将此字符串编码为字节序列,并将结果存储到新的字节数组中。

    所以无论何时:

    accentedE.getBytes()
    

    在默认 OS 代码页中,它以字节 encoded 的形式,在您的情况下为 cp-1252

    这一行:

    new String(accentedE.getBytes(), Charset.forName("UTF-8"))
    

    获取重音字节(以 cp1252 编码)并尝试以 UTF-8 解码它们,因此出现错误。另一边的情况相同:

    new String(accentedE.getBytes("utf-8"))
    

    getBytes 方法采用 cp-1252 编码的重音 E 字节,将它们重新编码为 UTF-8,然后字符串 constructor 使用默认的操作系统代码页 cp-1252 对它们进行编码。

    通过使用平台的默认字符集解码指定的字节数组来构造一个新的字符串。新字符串的长度是字符集的函数,因此可能不等于字节数组的长度。

    我强烈推荐阅读这篇优秀的文章:

    The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

    更新:

    简而言之,每个字符都存储为一个数字。为了知道哪个字符是哪个数字,操作系统使用代码页。考虑以下 sn-p:

    String accentedE = "é";
    
    System.out.println(String.format("%02X ", accentedE.getBytes("UTF-8")[0]));
    System.out.println(String.format("%02X ", accentedE.getBytes("UTF-8")[1]));
    System.out.println(String.format("%02X ", accentedE.getBytes("windows-1252")[0]));
    

    哪个输出:

    C3 
    A9 
    E9
    

    这是因为UTF-8 中的小重音 e 存储为值 C3A9 的两个字节,而在 cp-1252 中存储为值 E9 的单个字节。有关详细说明,请阅读链接文章。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多