Java App:无法正确读取iso-8859-1编码文件

时间:2009-01-31 10:51:40

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

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

5 个答案:

答案 0 :(得分:14)

我怀疑你的文件不是实际编码为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");

答案 1 :(得分: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)查看是否存在差异。

答案 2 :(得分:6)

@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
  2 O

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

  

UTF-8
  ôÃ'

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

5554 462d 380a f4c3 b40a

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

答案 3 :(得分:3)

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

答案 4 :(得分:1)

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

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