【问题标题】:Curly quotes causing Java Scanner hasNextLine() to be false -- why?导致 Java Scanner hasNextLine() 错误的花括号 - 为什么?
【发布时间】:2013-09-19 17:30:20
【问题描述】:

我在让 java.util.Scanner 读取我保存在记事本中的文本文件时遇到问题,尽管它与其他人一起工作正常。基本上,当它试图读取问题文件时,它完全空手而归—— hasNextLine() 是假的,缓冲区是空的,等等。我把它缩小到它甚至不会读取第一行的事实。是文件中anywhere 中的一个大引号。不抛出异常。请注意,同一文件上的 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”。在 Google 上的搜索让我尝试了这个:

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 无论如何都可以工作?

编辑:

下面的链接/cmets 确实帮助我指明了正确的方向!我想我已经弄明白了。

首先,在记事本中:

  • “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 没有,我猜这是因为扫描仪需要标记文件,即。解释字符,以便它可以识别空格和其他模式,所以当有一些无法识别的东西时它会窒息。读者不需要这样做。它需要识别的只是换行符。

【问题讨论】:

  • 很棒的文章可以帮助您前往understanding encodings
  • 如果你将string(不是file)传递给你的scanner,它也会被大括号卡住吗?
  • 如果我在我的代码 ("'") 中创建一个带有花引号作为字符串的扫描仪,是的,它似乎可以工作。大括号没有问题。

标签: java encoding utf-8


【解决方案1】:

如果您在创建扫描仪时未指定编码,它将尝试根据字节顺序标记 (BOM) 来判断编码,这是文件的前几个字节。如果没有,它将默认为操作系统使用的任何默认值。由于您使用的是 Windows,因此默认值为 cp-1252。似乎记事本正在使用 ISO-8859-1 保存您的文本文件,这与 cp-1252 相似,但不一样。有关详细信息,请参阅此链接:

http://www.i18nqa.com/debug/table-iso8859-1-vs-windows-1252.html

当您将其保存为 UTF-8 时,它可能会将 UTF-8 BOM 放在文件的开头,并且扫描仪可以拾取它。

如果您想深入了解 BOM,请在 wikipedia 中查找 - 这篇文章非常好。您还可以下载 PSPad 并以十六进制模式打开文本文件以查看各个字节。希望对您有所帮助:)

【讨论】:

  • 这还不够,但它确实让我走上了正轨,而且 PSPad 也很有帮助。我用我能弄清楚的东西编辑了我的问题。谢谢!
【解决方案2】:

ScannerhasNextLine 方法如果在输入文件中遇到编码错误,则只会返回 false。无一例外。这令人沮丧,并且在任何地方都没有记录,即使在 JDK 8 documentation 中也是如此。

如果您只想逐行读取文件,请改用这个:

final BufferedReader input = new BufferedReader(new InputStreamReader(new FileInputStream("inputfile.txt"), "inputencoding"));

while (true) {
    String line = input.readLine();
    if (line == null) break;
    // process line
}

input.close();

确保将上面的inputencoding 替换为文件的正确编码。很可能是utf-8ascii。即使编码不匹配,它也不会像Scanner 那样提前终止。

【讨论】:

    【解决方案3】:

    前段时间,我在用户编辑的配置文件中遇到了类似的问题。因为我不知道用户会使用哪种类型的编辑器,所以我试试这个:

    org.mozilla.universalchardet.UniversalDetector
    

    可从这里获得:

    https://code.google.com/p/juniversalchardet/
    

    检测字符编码并不是一件简单的事情,所以我不能确定这个库是否在任何情况下都有效,但对我来说已经足够了。看一下,也许会以某种方式帮助检测您的编码,然后将其设置为Scanner

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多