为什么Java会要求我再次按Enter键?

14

我一直在思考Scanner如何工作,经过一段时间的钻研后,总算理解了。以下是代码:

Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
String p = sc.nextLine();
System.out.println(s);
System.out.println(p);
System.out.println(sc.hasNextLine());

我所期望的:

Love is good  <- press ENTER
Love is blind <- press ENTER
Love is good  <- output
Love is blind <- output
false

拥有的:

Love is good   <- I press ENTER
Love is blind  <- I press ENTER
Love is good   <- output
Love is blind  <- output
<- press ENTER
true <- output

不理解的是:

  1. 为什么在立即打印此行 - System.out.println(sc.hasNextLine()); - 时,它让我再次按ENTER键
  2. 当没有更多行或符号时,它打印true而不是false

已经阅读了十几篇关于在nextInt()之后使用hasNextLine()以及关于nextInt()不消耗行中的最后一个符号的stackoverflow答案,但我不明白为什么,即使我在这里没有使用nextInt(),我仍然需要再按一次ENTER,并且为什么 hasNestLine() 是true。


构建一个没有下一行的示例非常容易:将输入写入文本文件,然后通过命令行将该文本文件管道传递到Java程序中。由于已经到达输入的末尾,因此您的程序将输出false。在手动会话中结束输入也是可能的,在我的Bash上,可以使用Ctrl+D来实现。 - amon
6个回答

14

使用 BufferedReader 替代 Scanner,如下所示:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

class Test {
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        String s = reader.readLine();
        String p = reader.readLine();
        System.out.println(s);
        System.out.println(p);      
        System.out.println(reader.ready());
    }
}

如果想要深入了解此事,请看这里这里。一般来说,当你需要解析内容(比如nextInt)时,请使用Scanner,否则请使用BufferedReaderBufferedReaderScanner快得多。

至于为什么会出现这种麻烦的行为,请参阅Scannerjavadoc

适用于您特定情况的部分是:

扫描操作可能会阻塞等待输入。

在您的情况下,这意味着:在Scanner读取前两行后,由于其方法调用sc.hasNextLine()Scanner正在等待输入。然后,当按下Enter键时,它收到了一个输入(因此根据文档,它在有输入时返回true)。由于在方法调用sc.hasNextLine()后您的程序中没有更多可执行行,程序结束,给人一种需要按两次Enter键的错觉。


哇,非常感谢!我以前从未使用过那个!我在请求所有这些来解决UVa在线评判器上的问题,并且使用BufferReader +解析似乎比使用Scanner更容易。谢谢。 - alekscooper
完成了,谢谢。没有nextInt()-nextLine()的问题,似乎更容易了许多。 - alekscooper
但这是否回答了原始问题? - Jasper
@Jasper,您能告诉我这里原始问题没有得到解答的地方吗?这样我就可以进行编辑了。 - Sнаđошƒаӽ

11

来自Scanner文档(重点是我):

public boolean hasNextLine()
如果这个扫描器的输入中还有另一行,则返回true。这个方法可能会阻塞等待输入。扫描器不会超越任何输入。 可能发生的情况是hasNextLine正在阻塞,因为它正在等待输入,你可以通过按回车键来输入。

谢谢您的回答!如果我需要使用Scanner,您能告诉我如何修复吗? - alekscooper

10

首先,您正在使用Scanner声明为System.in,这意味着您正在要求.in检查是否有更多的值,所以它会等待用户输入某些内容,而在按Enter键后,它显然会返回true。

因此,如果您想检查s或p,请像这样声明Scanner:

Scanner sc = new Scanner(s);
Scanner sc = new Scanner(p);

那么检查一下sc.hasNextLine();,据我所知,这应该返回false,但对于用户输入,我建议创建另一个Scanner变量,然后从用户那里获取值,并使用sc来进行你的hasNextline()操作。

希望第一个逻辑能对你有所帮助。


你正在扫描已经读取的行吗?请使用 System.in 而不是 s 和 p。 - Top Sekret
但兄弟,这就是为什么当他想要检查是否还有更多行并使用System.in时,他会得到true作为答案的原因,因为它每次都会返回true。 - Rohan Gill

9

您正在要求输入三行内容!

标准输入始终有一行额外的内容,直到您关闭终端。在Unix系统上,您可能可以在最后按Ctrl+D,它会返回false(=输入关闭)。

如果您有一个有限的输入,则程序将按预期运行。但是交互式输入:程序如何知道用户不打算输入Shakespeare等内容呢?

函数 hasNextLine() 将等待新的输入到达(或者等待不能有新的输入的信号)。这个函数不是用于“用户是否已经输入另一行”,而是意味着“等待下一行”。因此,实际上,hasNextLine() 相当于“用户输入仍然连接,并且可能最终包含另一行”。

为什么需要最后一行?只需将其删除即可!


我明白你的观点 :) 最后一行是为了理解 hasNextLine() 的工作原理。正如我之前所写,我已经开始在 UVa Online Judge 上解决问题,而评分人员对于你如何组织输入循环非常挑剔,因此我必须了解 Java 中的输入方式(我太菜了,无法用 C++ 解决问题)。 - alekscooper
1
当输入来自文件时,您可以期望hasNextLine最终为false(但是您不想在此竞赛中使用此API,因为它太慢了)。尽管如此,您可能会在结尾处有一个额外的行。 - Has QUIT--Anony-Mousse

6
您遇到的问题是,Scanner正在从给定的InputStream中读取数据,这可以是像文件或System.in这样的东西。
正如答案所描述的那样,文件有一个结尾,当使用sc.hasNextLine()时将返回false。 通过使用流,您期望它没有结束以便提供输入数据,因此将阻塞并等待您的下一个输入。
也许您可以考虑一些“退出字符”(在这里是“#”):
Scanner sc = new Scanner(System.in);
StringBuilder builder = new StringBuilder();
while (sc.hasNextLine()) {
    String line = sc.nextLine();
    if (line.equals("#")) {
        break;
    }
    builder.append(line + "\n");
}
System.out.println(builder.toString());
sc.close();

0

根据您的情况,我对您的示例进行了调试,发现

System.out.println(sc.hasNextLine());

程序行等待新的输入。它既不是真也不是假。它不存在。这就是为什么它会阻塞应用程序。无论你输入了什么(回车或任何其他键),它都会给你一个真值,因为你提供了一个新的输入。


2
它不是“null”,因为那样会打印出null。它还没有返回。 - Has QUIT--Anony-Mousse
是的,我知道。可能,这里存在比空值更大的问题。我只是想分享一下当它阻塞时这行代码的价值。 - Bernhard Colby
这行代码的“值”也不是null,但它并不存在。(原始类型的boolean不能为null,只有对象变量可以为null指针) - Has QUIT--Anony-Mousse
仍然不正确。按下哪些键是很重要的。例如,如果您键入“abc”,然后关闭管道(例如在Unix终端中通过Ctrl + D),它将返回false - Has QUIT--Anony-Mousse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接