Java:仅当通过标准输入给出 EOF (Ctrl+D) 时才退出内循环

问题描述 投票:0回答:1

我的程序有一个主 while 循环作为主要逻辑,还有一个内部 while 循环用于运行“命令函数”的逻辑。当位于内部 while 循环内部时,我希望

EOF
(Ctrl + D) 仅退出内部 while 循环并继续外部循环(以侦听更多命令)。然而,它从内部“命令”循环和外部主 while 循环中退出。

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

public class Main {
    public static void main(String[] args) {

        String line;
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        try {
            while ((line = reader.readLine()) != null) {

                if (line.compareTo("echo") == 0) {
                    while ((line = reader.readLine()) != null)
                        System.out.println("echo: " + line);

                    // Ctrl+D (EOF) will exit loop
                    System.out.println("Stop echo-ing");
                }
                else System.out.println("Cmd not recognized");

                // No EOF given, should not exit this loop
                System.out.println("Exit main loop");
            }

        } catch (IOException e) {
            System.err.println(e.getMessage());
        }
    }
}

重现问题:

  1. 复制并粘贴代码,然后运行它
  2. 输入
    echo
    进入内部while循环
  3. Ctrl
    +
    D
    EOF
    提供给标准输入

打印以下内容:

^D
Stop echo-ing
Exit main loop

意外地打印了“退出主循环”。

如何在按下

Ctrl
+
D
时仅退出内部 while 循环?

java stdin bufferedreader eof
1个回答
0
投票

你想要的东西是不可能的。 CTRL+D 不会“向您发送信号”,它会终止标准。它“关闭”

System.in
。内循环退出的原因是因为
.readLine()
返回
null
来表示“输入源已关闭”。不是因为它返回
null
来表示:“用户按下了 CTRL+D”。已经关闭的事物就已经关闭了。永久。您无法取消关闭它。

找到另一种方式来表示“我希望退出内循环”。例如,让用户输入

DONE
EXIT
或诸如此类。

如果这是不可接受的,请继续阅读替代解决方案。

信号

如果你坚持使用信号式的功能,java并没有将“操作系统有信号发送到进程”的想法提升到“这是通用的”水平。 Java 与平台无关,并且选择了基本上不公开底层操作系统提供的任何功能的路线,除非所有操作系统以 Java 可以统一的方式提供它。

但它就在那里 - 在一个专有的包中,它会警告您它将很快被删除。当然。十多年来,它一直在发出这样的警告;它在 Java21 中仍然有效:

// WARNING: You must hard-kill this process to end it!

import sun.misc.*;

class Test {
  public static void main(String[] args) throws Exception {
    Signal.handle(new Signal("INT"), new SignalHandler() {
      public void handle(Signal sig) {
        System.out.println("INT PRESSED!");
      }
    });

    while (true) {
      System.out.println("Main thread");
      Thread.sleep(1000L);
    }
  }
}

这将拦截 CTRL+C(它发送中断 (

INT
) 信号,并且“通常”由 JDK 通过运行所有关闭挂钩然后关闭 JVM 来处理)。 非常复杂:该代码在单独的线程中运行,因此需要向主线程发送消息。它还需要中断主线程,因为它正在“等待”依赖于操作系统的输入(如果没有指定

throws InterruptedException

,是否可以中断它取决于实现)。这是大量非常棘手的工作,您得到的只是可以按 CTRL+C(而不是 CTRL+D!)退出内循环,以及这在未来的 java 版本中不起作用的包袱。

我强烈建议你不要这样做。

煮熟模式

进程的标准(在java中,这被公开为

System.in

)可以是任何东西;如果您运行

java myapp <somefile.txt
,那么标准输入就是该文件的内容。但是,默认情况下,它是键盘。在这种情况下,Java 以所谓的“熟模式”开始其终端交互。在用户按下 Enter 键之前,您根本无法获得任何输入,但例如,用户可以编辑他们的输入。
您可以将自己切换到原始模式(“未煮熟”模式)。在原始模式下,您可以在按下每个按键时获取它们,并且您可以发送终端命令,例如移动光标,更改下一个要打印的内容的颜色(甚至背景颜色),等等。

在原始模式下,CTRL+D 和通常的 CTRL+C 会像任何其他符号一样简单地发送到您的应用程序 - 通常,使用 unicode 值 z,其中 z 是字母表中的字母(CTRL+A 发送 1,CTRL+ Z 发送 26)。例如,

in.read()

将返回 1。不是字符“1”,而是数字 1。

您可能不想要 

readLine()

那么,这不会帮助您检测 CTRL+D。您只需要

read()
,在检查您阅读的内容后,将其添加到
StringBuilder
(如果他们输入普通字母),
处理输入(如果他们输入 Enter,那就是 in.read() == '\n' ),
如果是 4,则响应 CTRL+D 做任何你想做的事情。比如 break 内循环。
烹饪模式带来了很多好处,但你必须手写它们。此外,转向原始模式取决于操作系统,而 java 没有能力做到这一点。

这也非常复杂,我强烈建议您也不要这样做。但是,

如果你真的想为此烧伤自己,请阅读所有内容

不,不!我想要简单的东西

不要向信使开枪,但是

没有简单的答案

。这是两个非常复杂的解决方案之一,或者,忘记使用 CTRL+D 作为非终端信号机制。

© www.soinside.com 2019 - 2024. All rights reserved.