从控制台读取时忽略早期输入

Ignore earlier input when reading from console

我想向控制台输出一个问题,然后在输出问题后获取下一行输入。

例如,我的程序可能正在休眠或进行一些耗时的计算,而当用户正在等待时,他们可能会决定在控制台中键入一些注释(可能没有按回车键,或者可能是多行)。一旦睡眠完成,程序就会问用户一个问题,"What is your name?" 然后它应该等待包含用户名的 next 行输入,并忽略任何随机用户在睡眠过程中所做的笔记。

下面是一些尝试这样做的代码:

public static void main(String[] args) throws InterruptedException {
    Scanner scanner = new Scanner(System.in);
    Thread.sleep(10_000);
    System.out.println("What is your name?");
//  while (scanner.hasNext()) {
//      scanner.nextLine();
//  }
    String name = scanner.nextLine();
    System.out.println("Hi, " + name);
}

当我在睡眠期间键入几行时,其行为如下:

gpeo
hpotWhat is your name?
Hi, gpeo

问题是扫描仪将从它读取的最后一个输入继续读取下一个输入,而不是从最后一个 System.out.println()(这是有道理的)。注释掉的代码试图通过首先读取过去所有较早的输入来纠正该问题,然后再等待一行以分配给 name。然而,scanner.hasNext() 并没有像我希望的那样工作,因为当没有下一个标记时它不会简单地 return false 而是等待另一个标记(所以我不知道为什么它会困扰 return 一个布尔值)。

另一件让我感到困惑的事情是,在睡眠期间,如果您在一行中键入内容,那么 实际上会被忽略:

brbr irgjojWhat is your name?
A
Hi, A

我以为它会输出 Hi, brbr irgjojA,所以这让我觉得我可能误解了控制台输入和 Scanner 的工作方式。

编辑:最后一个示例来自 IntelliJ 中的 运行。当我从 Bash 命令行 运行 而不是时,我得到 Hi, brbr irgjojA。第一个示例的输出没有改变。

此外,有人问我这个问题是否与 this 相同,显然我必须解释为什么它不在这里,否则它会出现在问题上。 post(和其他类似的)的问题是 he/she 将 scanner.nextLine()scanner.nextInt() 和类似的方法混合在一起,这些方法不会读取整行或行尾。我只使用 nextLine() 读取输入,我的问题完全不同。

进一步编辑

我设法丢弃了基于 this answer 的随机笔记的第一行到另一个问题。这是新代码:

public static void main(String[] args) throws InterruptedException, IOException {
    Scanner scanner = new Scanner(System.in);
    Thread.sleep(10_000);
    System.out.println("What is your name?");
    while (System.in.available() > 0) {
        scanner.nextLine();
    }
    String name = scanner.nextLine();
    System.out.println("Hi, " + name);
}

以下是 IntelliJ 中的一些测试 运行:

grgWhat is your name?
A
Hi, A

ghr
rhWhat is your name?
A
Hi, A

rghr
hrh
htWhat is your name?
Hi, hrh

uirh
iw
hjrt
sfWhat is your name?
Hi, iw

下面是 Bash 中的类似测试:

htrWhat is your name?
A
Hi, htrA

rgj
hrWhat is your name?
A
Hi, hrA

rjkh
ry
jWhat is your name?
Hi, ry

ryi
rj
rd
jrWhat is your name?
Hi, rj

如您所见,while 循环内的行似乎从未因为某种原因被执行超过一次。我尝试在循环内添加睡眠或使用其他 InputStream 方法,如 skip()readAllBytes(),但这些似乎根本没有帮助。

我认为对于 Bash 的问题,对于不完整的行可能没有什么可以做的,但我确信必须有一种方法可以丢弃所有已完成的行(而不是只是第一个)。该解决方案不必使用 Scanner,它应该按预期运行。

Scanner使用缓冲区。它的默认大小是 1024 个字符。因此,通过第一个 nextLine() 调用,它将最多 1024 个可用字符读入缓冲区。这是必要的,因为 Scanner 在填充缓冲区并在缓冲区中搜索换行符之前甚至不知道下一行有多少字符。

因此,如果待定字符少于缓冲区大小,则循环只会迭代一次。但即使有更多的字符和更多的循环迭代,结果状态也可能是缓冲区中有一些待处理的行。

只要 Scanner 的缓冲区处于初始空状态,您就可以直接刷新源流,而不是使用扫描器:

Scanner scanner = new Scanner(System.in);
Thread.sleep(10_000);
while(System.in.available() > 0) {
    System.in.read(new byte[System.in.available()]);
}
System.out.println("What is your name?");
String name = scanner.nextLine();
System.out.println("Hi, " + name);

请注意,使用 System.in.skip(System.in.available()); 而不是 read 是很自然的,但是在尝试时,我遇到了一个错误,即底层流在之后没有更新 available() 计数a skip 从控制台读取时。

请注意,如果 Scanner 未处于其初始状态但已经有一些缓冲内容,则无法刷新缓冲区,因为其 API 旨在不区分缓冲和尚未缓冲,因此任何匹配所有内容的尝试都会导致再次从源读取(并阻塞)。摆脱缓冲内容的最简单解决方案是
scanner = new Scanner(System.in);