我一直在让 java.util.Scanner 读取我保存在记事本中的文本文件时遇到问题,即使它与其他人一起工作得很好。基本上,当它尝试读取问题文件时,它完全空手而归—— hasNextLine() 为假,缓冲区为空等。我将其缩小到它甚至不会读取第一行的事实是文件中任意位置的花引号。不抛出异常。请注意,同一文件上的 BufferedReader 没有问题。
try {
int count = 0;
Scanner scanner = new Scanner(new File("C:/myfile.txt"));
while (scanner.hasNextLine()) {
count++;
scanner.nextLine();
}
scanner.close();
System.out.print(count);
count = 0;
BufferedReader reader = new BufferedReader(new FileReader("C:/myfile.txt"));
while (reader.readLine() != null) {
count++;
}
reader.close();
System.out.print(count);
}
catch(IOException e) {
e.printStackTrace();
}
上面的代码读取一个只包含一个大引号的文件,打印出“01”。在谷歌上的搜索让我尝试了这个:
Scanner scanner = new Scanner(new File("C:/myfile.txt"), "ISO-8859-1");
这使它工作(即它打印出“11”)。我还注意到,如果我进入记事本并执行另存为...底部的默认编码是“ANSI”。如果我将其更改为“UTF-8”并保存文件,那么扫描仪(没有编码)也可以工作。如果我告诉扫描仪“UTF-8”,那么可以理解,它只有在我保存为 UTF-8 时才有效,但即使我将其保存为“ANSI”,“ISO-8859-1”似乎也能让它工作。
所以,我知道它与文件编码有关,但问题是我对文件编码一无所知。我对“ISO-8859-1”意味着什么的了解非常模糊。为什么无论我如何保存文件,它都能正常工作?为什么 BufferedReader 无论如何都可以工作?
编辑:
下面的链接/评论真的帮助我指出了正确的方向!我想我已经弄清楚了。
首先,在记事本中:
- “ANSI”是 CP1252
- “Unicode”是 UTF-16LE
- “UTF-8”是……嗯,UTF-8
在十六进制中,卷曲撇号表示为:
- CP1252:92
- UTF-16LE:1920
- UTF-8:E2 80 99
根据 Charset.defaultCharset(),Java 在我的系统上使用的默认编码是 UTF-8。因此,当我将文件保存为 UTF-8 时,扫描仪就知道会发生什么。但是,当我将文件保存在 CP1252 中时,它在达到“92”时就窒息了,因为它不是以该编码表示字符的有效方式。只要文件中没有任何这样的字符,它就可以正常工作——“hello world”的十六进制在 CP1252 和 UTF-8 中恰好相同,并且不会导致问题。
UTF-8 不适用于 UTF-16 文件,因为它不知道如何处理字节顺序标记 ("FFFE"),无论文件中包含什么字符。
另一方面,当我将扫描仪设置为 CP1252 或 ISO-8859-1 时,它的容忍度要高得多。请注意,它不一定能正确解释字符,但没有什么能阻止它识别文件中的行并循环遍历。
至于为什么 Scanner 有问题但 FileReader/BufferedReader 没有问题,我猜这是因为扫描仪需要标记文件,即。解释字符,以便它可以识别空格和其他模式,所以当有无法识别的东西时它会窒息。读者不需要这样做。它需要识别的只是换行符。