Java中的UTF-8和UTF-16

时间:2012-10-18 02:55:41

标签: java string encoding utf-8

我真的希望下面的字节数据显示不同,但实际上它们是相同的,根据wiki http://en.wikipedia.org/wiki/UTF-8#Examples,字节中的编码看起来不同,但是为什么Java会将它们打印出来?< / p>

    String a = "€";
    byte[] utf16 = a.getBytes(); //Java default UTF-16
    byte[] utf8 = null;

    try {
        utf8 = a.getBytes("UTF-8");
    } catch (UnsupportedEncodingException e) {
        throw new RuntimeException(e);
    }

    for (int i = 0 ; i < utf16.length ; i ++){
        System.out.println("utf16 = " + utf16[i]);
    }

    for (int i = 0 ; i < utf8.length ; i ++){
        System.out.println("utf8 = " + utf8[i]);
    }

4 个答案:

答案 0 :(得分:8)

尽管Java在内部将字符保存为UTF-16,但当您使用String.getBytes()转换为字节时,每个字符都使用默认的平台编码进行转换,这可能类似于windows-1252。我得到的结果是:

utf16 = -30
utf16 = -126
utf16 = -84
utf8 = -30
utf8 = -126
utf8 = -84

这表示我的系统上的默认编码是“UTF-8”。

另请注意,String.getBytes()的文档包含以下注释:The behavior of this method when this string cannot be encoded in the default charset is unspecified.

但一般情况下,如果您始终指定与a.getBytes("UTF-8")

相同的编码,则可以避免混淆

另外,另一件可能引起混淆的事情是在源文件中直接包含Unicode字符:String a = "€";。该欧元符号必须编码为存储为文件中的一个或多个字节。当Java编译您的程序时,它会看到这些字节并将它们解码回欧元符号。你希望。你必须确保将欧元符号保存到文件中的软件(记事本,eclipse等)以与Java期望的相同的方式对其进行编码.UTF-8正变得越来越流行但它不是通用的许多编辑都不会用UTF-8编写文件。

答案 1 :(得分:4)

  

一个好奇心,我想知道JVM如何知道原始的默认字符集......

JVM用于确定初始默认字符集的机制是特定于平台的。在UNIX /类UNIX系统上,它由LANG和LC_ *环境变量决定;见man locale


  

Ermmm ..此命令用于检查特定操作系统中的默认字符集是什么?

这是正确的。但我告诉过你,因为手动条目描述了如何默认编码是由环境变量决定的。

回想起来,这可能不是您原始评论的意思,但这是指定平台默认编码的方式。 (对于单个文件,&#34;默认字符集&#34;的概念没有意义;见下文。)

  

如果说我有10个Java源文件,其中一半保存为UTF-8,其余保存为UTF-16,编译完成后,我将它们(类文件)移动到另一个OS平台,现在JVM如何知道他们的默认编码?是否将默认的字符集信息包含在Java类文件中?

这是一组相当混乱的问题:

  1. 文本文件没有默认字符集。它有一个字符集/编码。

  2. 非文本文件根本没有字符编码。这个概念毫无意义。

  3. 没有100%可靠的方法来确定文本文件的字符编码是什么。

  4. 如果你不告诉java编译器文件的编码是什么,它会认为它是平台的默认编码。编译器不会试图再次猜测你。如果编码不正确,编译器可能会或可能不会注意到您的错误。

  5. 字节码(&#34; .class&#34;)文件是二进制文件(见2)。

  6. 当字符和字符串文字被编译成&#34; .class&#34;文件,它们现在以不受平台默认编码影响的方式或您可以影响的任何其他方式表示。

  7. 如果您在编译时使用源文件编码时出错,则无法在&#34; .class&#34;中修复它。文件级别。您唯一的选择是返回并重新编译类,告诉Java编译器正确的源文件编码。

  8. &#34;假设我有10个Java源文件,其中一半保存为UTF-8,其余保存为UTF-16&#34;
    就是不要这样做!

    • 不要将源文件保存在混合编码中。你会疯狂的。
    • 根本没有理由把文件存储在UTF-16中......

  9.   

    所以,我很困惑,虽然人们说&#34;平台依赖&#34;,它是否与源文件有关?

    平台相关意味着它可能取决于操作系统,JVM供应商和版本,硬件等。

    它不一定与源文件有关。 (任何给定源文件的编码可能与默认字符编码不同。)

      

    如果不是,我该如何解释上述现象?无论如何,上面的混淆将我的问题扩展到&#34;所以,在我将源文件编译成类文件后会发生什么,因为类文件可能不包含编码信息,所以现在结果实际上依赖于&#39; platform& #39;但不再是源文件了吗?&#34;

    平台特定机制(例如环境变量)确定java编译器将其视为默认字符集。除非你覆盖它(例如通过在命令行上为java编译器提供选项),否则Java编译器将使用它作为源文件字符集。但是,这可能不是源文件的正确字符编码;例如如果您在具有不同默认字符集的其他计算机上创建它们。如果java编译器使用错误的字符集来解码您的源文件,则可能会将错误的字符代码放入&#34; .class&#34;文件。

    &#34; .class&#34;文件不依赖于平台。但是如果它们被错误地创建,因为你没有告诉Java编译器源文件的正确编码,那么&#34; .class&#34;文件将包含错误的字符。


      

    你的意思是:&#34; &#34;默认字符集的概念&#34;对于单个文件没有意义&#34;?

    我说是因为它是真的!

    默认字符集表示您未指定字符集时使用的字符集。

      

    但我们可以控制我们希望如何存储文本文件?即使使用记事本,也可以选择编码。

    这是正确的。那就是你告诉记事本用于文件的字符集。如果您没有告诉它,记事本将使用默认字符集来写入文件。

    GUESS的记事本中有一点黑魔法,当它读取文本文件时,字符编码是什么。基本上,它会查看文件的前几个字节,以查看它是否以UTF-16字节顺序标记开头。如果它看到一个,它可以启发式地区分UTF-16,UTF-8(由Microscoft产品生成)和&#34;其他&#34;。但它无法区分不同的&#34;其他&#34;字符编码,并且它不会将UTF-8识别为不以BOM标记开头的文件。 (UTF-8文件上的BOM是特定于Microsoft的约定......如果Java应用程序读取文件并且不知道跳过BOM字符,则会导致问题。)

    无论如何,问题不在于写入源文件。它们发生在Java编译器使用不正确的字符编码读取源文件时。

答案 2 :(得分:3)

你正在处理一个糟糕的假设。 getBytes()方法不使用UTF-16编码。它使用平台默认编码。

您可以使用java.nio.charset.Charset.defaultCharset()方法进行查询。在我的情况下,它是UTF-8,对你也应该是一样的。

答案 3 :(得分:1)

如果找不到特定于平台的编码,则默认为UTF-8ISO-8859-1。不是UTF-16。所以最终你只在UTF-8进行字节转换。 这就是你byte[]匹配的原因 您可以使用

找到默认编码
 System.out.println(Charset.defaultCharset().name());