7

我有一个编码为 iso-8859-1 的文件,其中包含 ô 等字符。

我正在使用 java 代码读取此文件,例如:

File in = new File("myfile.csv");
InputStream fr = new FileInputStream(in);
byte[] buffer = new byte[4096];
while (true) {
    int byteCount = fr.read(buffer, 0, buffer.length);
    if (byteCount <= 0) {
        break;
    }

    String s = new String(buffer, 0, byteCount,"ISO-8859-1");
    System.out.println(s);
}

然而 ô 字符总是乱码,通常打印为 ? .

我已经阅读了这个主题(并在途中学到了一点),例如

但仍然无法正常工作

有趣的是,这适用于我的本地电脑(xp),但不适用于我的 linux 机器。

我检查了我的 jdk 是否支持所需的字符集(它们是标准的,所以这并不奇怪)使用:

System.out.println(java.nio.charset.Charset.availableCharsets());
4

5 回答 5

15

我怀疑您的文件实际上没有编码为 ISO-8859-1,或者 System.out 不知道如何打印字符。

我建议首先检查文件中的相关字节。要检查第二个,检查字符串中的相关字符,将其打印出来

 System.out.println((int) s.getCharAt(index));

在这两种情况下,结果都应该是十进制的 244;0xf4 十六进制。

一般建议见我关于 Unicode 调试的文章(提供的代码是 C#,但很容易转换为 Java,原理相同)。

一般来说,顺便说一句,我会InputStreamReader用正确的编码来包装流 - 这比“手动”创建新字符串更容易。我意识到这可能只是演示代码。

编辑:这是证明控制台是否可以工作的一种非常简单的方法:

 System.out.println("Here's the character: \u00f4");
于 2009-01-31T10:59:35.977 回答
9

将文件解析为固定大小的字节块并不好——如果某个字符的字节表示跨越两个块怎么办?使用InputStreamReader适当的字符编码代替:

 BufferedReader br = new BufferedReader(
         new InputStreamReader(
         new FileInputStream("myfile.csv"), "ISO-8859-1");

 char[] buffer = new char[4096]; // character (not byte) buffer 

 while (true)
 {
      int charCount = br.read(buffer, 0, buffer.length);

      if (charCount == -1) break; // reached end-of-stream 

      String s = String.valueOf(buffer, 0, charCount);
      // alternatively, we can append to a StringBuilder

      System.out.println(s);
 }

顺便说一句,记得检查 unicode 字符是否确实可以正确显示。您还可以将程序输出重定向到一个文件,然后将其与原始文件进行比较。

正如Jon Skeet所暗示的,这个问题也可能与控制台有关。试试看System.console().printf(s)有没有区别。

于 2009-01-31T11:18:17.263 回答
6

@Joel -您自己的回答确认问题出在您的操作系统上的默认编码(UTF-8,Java 采用的那个)和您的终端使用的编码(ISO-8859-1)之间的差异。

考虑这段代码:

public static void main(String[] args) throws IOException {
    byte[] data = { (byte) 0xF4 };
    String decoded = new String(data, "ISO-8859-1");
    if (!"\u00f4".equals(decoded)) {
        throw new IllegalStateException();
    }

    // write default charset
    System.out.println(Charset.defaultCharset());

    // dump bytes to stdout
    System.out.write(data);

    // will encode to default charset when converting to bytes
    System.out.println(decoded);
}

默认情况下,我的 Ubuntu (8.04) 终端使用 UTF-8 编码。使用此编码,将打印:

UTF-8

如果我将终端的编码切换为 ISO 8859-1,则会打印:

UTF-8
ôô

在这两种情况下,Java 程序都会发出相同的字节:

5554 462d 380a f4c3 b40a

唯一的区别在于终端如何解释它接收到的字节。在 ISO 8859-1 中,ô 编码为 0xF4。在 UTF-8 中,ô 编码为 0xC3B4。其他字符对两种编码都是通用的。

于 2009-01-31T15:40:00.183 回答
3

如果可以,请尝试在调试器中运行程序,以查看创建后的 's' 字符串中的内容。它可能具有正确的内容,但在 System.out.println(s) 调用后输出是乱码。在这种情况下,Java 认为的输出编码与 Linux 上终端/控制台的字符编码之间可能存在不匹配。

于 2009-01-31T10:59:30.360 回答
1

基本上,如果它可以在您的本地 XP PC 上运行,但不能在 Linux 上运行,并且您正在解析完全相同的文件(即您以二进制方式在盒子之间传输它),那么它可能与 System.out 有关。 println 调用。我不知道您如何验证输出,但如果您通过从 XP 框中连接远程 shell 来验证输出,则需要考虑 shell(和客户端)的字符集。

此外,Zach Scrivena 的建议也是正确的——你不能假设你可以以这种方式从数据块创建字符串——要么使用 InputStreamReader,要么首先将完整的数据读入数组(显然不适用于大文件) . 但是,由于它似乎确实可以在 XP 上运行,所以我敢冒昧地说,在这种特定情况下,这可能不是您的问题。

于 2009-01-31T11:36:55.057 回答