Scanner "not advance past any input" 的 hasNext 方法如何?

How can the hasNext methods of Scanner "not advance past any input"?

java.util.ScannerhasNexthasNextXxx 方法中显示:

Returns true if this scanner has another token in its input. This method may block while waiting for input to scan. The scanner does not advance past any input.

当扫描器显然必须检查输入中是否存在下一个标记(如 ReadableInputStream)时,它如何不通过输入流前进?

确实通过输入流前进。从 ScannerScanner 用户的角度来看,它只是不会超过下一个输入。所以可以说这篇文档措辞不当。

只需将一个标记放入流中,然后为该标记调用 hasNext,即可轻松测试这一点。在那种情况下,您会发现调用 hasNext 后流已耗尽。

换句话说,Scanner 在内部缓冲查看的字符,包括任何分隔符。随后的调用将从缓冲的字符开始。 Scanner 不依赖于底层流提供的任何缓冲。

此文本至少存在到 Java 12。

示例代码:

Reader stringReader = new StringReader("Single line");

Scanner firstScanner = new Scanner(stringReader);
// prints out true because there is a first line
System.out.println(firstScanner.hasNextLine());

Scanner secondScanner = new Scanner(stringReader);
// prints out false because the scanner has advanced the reader
// ... through the character stream generated from the string
System.out.println(secondScanner.hasNextLine());

// prints out true because the first scanner is buffering the input
System.out.println(firstScanner.hasNextLine());

它还包含 hasNext 的其他文档问题,例如在 Scanner class 本身的文档中它是这样写的:

The next() and hasNext() methods and their companion methods (such as nextInt() and hasNextInt()) first skip any input that matches the delimiter pattern, and then attempt to return the next token.

显然最后一部分不适合 hasNext()。这看起来 hasNext 已被硬塞进一个现有的句子中。

这里有两个“通过输入推进”的概念。

第一个概念是从 InputStream 读取更多字节,当然 Scanner 必须这样做才能测试它是否有下一个标记。 Scanner 通过缓冲从 InputStream 读取的新字节来执行此操作,以便稍后可以在必要时从 Scanner 读取这些字节。我们可以称之为“通过输入流前进”。

第二个概念是将要从缓冲区中消耗的下一个字节的位置提前。当您调用像 next 这样消耗令牌的方法时,会发生这种情况,以标记这些字节已被“使用”并且不应再次读取;调用 next 会增加缓冲区中的位置。我们可以称之为“通过扫描器缓冲区中的输入前进”。

文档的声明“扫描器不会前进超过任何输入” 是第二个概念;调用 hasNext 不会在扫描器的缓冲区中前进,它只是从输入流中读取更多字节到缓冲区中。要验证这一点,您可以尝试创建一个输入流,调用 hasNext,然后使用 nextInLine:

读取下一个字符(而不是下一个标记)
> InputStream is = new ByteArrayInputStream("hello world".getBytes());
> Scanner sc = new Scanner(is);
> sc.next()
"hello" (String)
> sc.hasNext()
true (boolean)
> sc.nextInLine(".")
" " (String)

所以调用hasNext不仅不会消耗下一个token,而且也不会消耗下一个token之前的分隔符。在这个意义上,扫描仪没有“提前[d]超过任何输入”