【问题标题】:Java App : Unable to read iso-8859-1 encoded file correctlyJava 应用程序:无法正确读取 iso-8859-1 编码文件
【发布时间】:2010-10-04 15:50:51
【问题描述】:

我有一个编码为 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());

【问题讨论】:

  • 我应该补充一点,如果我只是 cat 文件的内容,我可以使用我的 linux 终端正确查看字符或原始文件
  • 您的终端使用什么字符编码?
  • 有趣的是——如果我添加运行时 java 属性“-Dfile.encoding=UTF16”,它会按预期工作,尽管我不明白这为什么重要——而且我不认为这是一个解决方案,但更多的是黑客。它不适用于设置为 UTF8 的属性。

标签: java encoding character-encoding iso-8859-1


【解决方案1】:

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

我建议首先检查文件中的相关字节。要检查第二个,检查字符串中的相关字符,用

打印出来
 System.out.println((int) s.getCharAt(index));

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

一般建议见my article on Unicode debugging(提供的代码是C#,但很容易转换成Java,原理相同)。

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

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

 System.out.println("Here's the character: \u00f4");

【讨论】:

  • 已使用linux文件工具测试文件类型:file --mime FranceJ2.csv FranceJ2.csv: text/plain; charset=iso-8859-1 并确认我可以正确阅读它,比如 vi,但我会听从你的建议。
  • 不要相信那些试图自动检测字符编码的工具。它们总是基于启发式,而且必须如此。他们不知道您的文件真正要包含什么文本。
  • 文件的 hexdump 产生:0000000 0df4 000a(有什么建议!?)
  • 就像 Jon 在他的文章中建议的那样,验证每一步的数据。如果您不在调试器中运行代码,您可以将十六进制字节转储到控制台,以确保您拥有您期望的真实数据。 (特别是如果它这么小)
  • 正如建议的字符的十进制值为 244。这很神秘,因为它表明乱码发生在 sys.out 调用期间或终端本身。我知道它不是终端,因为我可以 cat 文件并查看其内容没有问题。嗯
【解决方案2】:

将文件解析为固定大小的字节块并不好 --- 如果某个字符的字节表示跨越两个块怎么办?请改用具有适当字符编码的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)看看有没有区别。

【讨论】:

    【解决方案3】:

    @Joel - your own answer 确认问题出在您的操作系统上的默认编码(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。其他字符对两种编码都是通用的。

    【讨论】:

    • 我肯定在这里遗漏了一些东西 - 5554 462d 380a f4c3 b40a 转储是什么?当然不是System.out.write(data) 电话?
    • @Mr_and_Mrs_D 这些是 JRE 通过对 System.out 的所有三个调用写入设备 (STDOUT) 的字节。 0A 字节标记println 写入的换行符。 问题作者写了一个答案,已删除,但我认为能够阅读它增加了很多。
    • 感谢您的跟进-我知道自删除以来作者有一个答案-无法阅读-谢谢:)
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

        猜你喜欢
        • 2020-08-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-20
        • 1970-01-01
        • 1970-01-01
        • 2019-05-02
        • 2011-05-12
        相关资源
        最近更新 更多