Java UTF-8奇怪行为

6

我正在尝试在Java中解码一些UTF-8字符串。 这些字符串包含一些组合Unicode字符,例如CC 88(组合分音符)。 根据http://www.fileformat.info/info/unicode/char/0308/index.htm,字符序列似乎没问题。

但是转换为字符串后输出无效。 有什么想法吗?

byte[] utf8 = { 105, -52, -120 };
System.out.print("{{");
for(int i = 0; i < utf8.length; ++i)
{
    int value = utf8[i] & 0xFF;
    System.out.print(Integer.toHexString(value));
}
System.out.println("}}");
System.out.println(">" + new String(utf8, "UTF-8"));

Output:

    {{69cc88}}
    >i?
4个回答

9

你输出到的控制台(例如 windows)可能不支持 Unicode,可能会破坏字符。控制台输出并不是数据的良好表示。

尝试将输出写入文件,确保 FileWriter 上的编码是正确的,然后在支持 Unicode 的编辑器中打开文件。

或者,使用调试器确保字符符合预期。只是不要信任控制台。


在Ubuntu 9.04中的终端(gnome-terminal)中,输出应该是您预期的带有分音符的字母i。 - Joachim Sauer
我喜欢这个词“分音符号”。也许我以后会在谈话中更经常地使用它。 - skaffman

5

以下是我在Windows上使用Eclipse最终解决该问题的方法:

  1. 点击运行配置
  2. 点击参数选项卡。
  3. 添加 -Dfile.encoding=UTF-8
  4. 点击共同选项卡。
  5. 控制台编码设置为UTF-8

修改代码:

byte[] utf8 = { 105, -52, -120 };
System.out.print("{{");
for(int i = 0; i < utf8.length; ++i)
{
    int value = utf8[i] & 0xFF;
    System.out.print(Integer.toHexString(value));
}
System.out.println("}}");

PrintStream sysout = new PrintStream(System.out, true, "UTF-8");
sysout.print(">" + new String(utf8, "UTF-8"));

输出:

{{69cc88}}
> ï

如果您打算使用PrintStream自行编码数据,则不需要使用“-Dfile.encoding=UTF-8”开关。(手动设置“file.encoding”属性可能会对需要知道系统编码的任何代码造成问题。) - McDowell

4
代码没问题,但正如skaffman所说,你的控制台可能不支持适当的字符。
为了确定,你需要打印出该字符的Unicode值:
public class Test {
    public static void main(String[] args) throws Exception {
        byte[] utf8 = { 105, -52, -120 };
        String text = new String(utf8, "UTF-8");
        for (int i=0; i < text.length(); i++) {
            System.out.println(Integer.toHexString(text.charAt(i)));
        }
    }
}

这将打印出 69, 308 - 这是正确的 (U+0069, U+0308)。


1

Java 在将 Unicode 字符写入标准输出之前,将其编码为本地系统编码字节。像许多 Linux 发行版一样,某些操作系统使用 UTF-8 作为默认字符集,这很好。

出于各种向后兼容的原因,在 Windows 上情况有所不同。默认系统编码将是其中一个 "ANSI" 代码页,如果您打开默认命令提示符 (cmd.exe),它将是旧的 "OEM" DOS 代码页之一(尽管可以通过一些工作获取 ANSI 和 Unicode {{link3:with a bit of work}})。

由于U+0308不在任何“ANSI”字符集中(在您的情况下可能是1252),它将被编码为错误字符(通常是问号)。

将组合序列U+0069 U+0308规范化为单个字符U+00EF是启用Unicode的替代方法:

  public static void emit(String foo) throws IOException {
    System.out.println("Literal: " + foo);
    System.out.print("Hex: ");
    for (char ch : foo.toCharArray()) {
      System.out.print(Integer.toHexString(ch & 0xFFFF) + " ");
    }
    System.out.println();
  }

  public static void main(String[] args) throws IOException {
    String foo = "\u0069\u0308";
    emit(foo);
    foo = Normalizer.normalize(foo, Normalizer.Form.NFC);
    emit(foo);
  }

windows-1252 下,此代码将会输出:

字面量:i?
十六进制:69 308 
字面量:ï
十六进制:ef 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接