我的程序有一个主 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());
}
}
}
重现问题:
echo
进入内部while循环打印以下内容:
^D
Stop echo-ing
Exit main loop
意外地打印了“退出主循环”。
如何在按下 Ctrl + D 时仅退出内部 while 循环?
你想要的东西是不可能的。 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 版本中不起作用的包袱。
我强烈建议你不要这样做。煮熟模式
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 作为非终端信号机制。