Java App:无法正确读取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); } 

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

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

  • http://www.joelonsoftware.com/articles/Unicode.html
  • http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058
  • http://www.ingrid.org/java/i18n/utf-16/

但仍然无法使这个工作

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

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

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

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

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

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

在这两种情况下,结果为244十进制; 0xf4hex。

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

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

编辑:这是一个非常简单的方法来certificate控制台是否可以工作:

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

将文件解析为固定大小的字节块并不好 – 如果某个字符的字节表示跨越两个块会怎么样? 使用具有适当字符编码的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)来查看是否存在差异。

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

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

UTF-8
ôÃ’

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

 5554 462d 380a f4c3 b40a 

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

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

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

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