我有一个编码为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());
答案 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上运行,那么我冒昧地认为这可能不是你在这个特定情况下的问题。