Java中如何从控制台读取单个字符(随用户输入)?

137

有没有一种简单的方法在Java中从控制台读取单个字符,就像用户在输入时一样?这可能吗?我尝试了这些方法,但它们都等待用户按下 enter 键:

char tmp = (char) System.in.read();
char tmp = (char) new InputStreamReader(System.in).read ();
char tmp = (char) System.console().reader().read();           // Java 6

我开始觉得System.in在用户按下回车键之前不知道用户的输入内容。

7个回答

68
你需要做的是将控制台切换到“原始”模式(绕过行编辑且不需要按回车键),而不是“表格”模式(需要按回车键进行行编辑)。在UNIX系统上,'stty'命令可以更改模式。
现在关于Java...请参见Python和Java中的非阻塞控制台输入。摘录如下:
  

如果您的程序必须基于控制台,   您必须将终端从行模式切换到字符模式,   并记得在程序退出之前将其恢复。   没有跨操作   系统这样做的便携式方式。

其中建议之一是使用JNI。再次强调,这不是非常便携。线程末尾的另一个建议,并与上面的帖子共同,是查看jCurses的使用。

4
JCurses 也不是很具有可移植性...根据 JCurses 的 README 文件所述:"JCurses 由两部分组成:独立于平台的部分和依赖于平台的部分,后者由一个原生共享库组成,为前者提供原始的输入和输出操作。" - Ryan Fernandes
6
@RyanFernandes听起来对我来说相当便携- 单个工具可以在多个系统上运行(使用不同的依赖项)。 - Antoniossss

30

在Linux下对我来说运行良好。 - MrSmith42
7
在Mac上同样适用。你可能需要提到当程序需要恢复缓冲模式时应运行stty cooked </dev/tty,并且一定要在程序退出之前运行。 - Kelvin

23

我写了一个Java类RawConsoleInput,它使用JNA调用Windows和Unix/Linux操作系统函数。

  • 在Windows上,它使用msvcrt.dll中的_kbhit()_getwch()
  • 在Unix上,它使用tcsetattr()将控制台切换到非规范模式,使用System.in.available()检查是否有数据可用,并使用System.in.read()从控制台读取字节。使用CharsetDecoder将字节转换为字符。

它支持非阻塞输入,并混合原始模式和普通行模式输入。


这个被测试/压力测试的程度有多大? - anon
2
@QPaysTaxes 压力测试对于控制台输入来说是很困难的。我认为,在这种情况下,更重要的是在各种环境中进行测试(不同的Windows/Linux版本,64/32位,通过SSH、Telnet、串口或桌面控制台使用Linux等)。到目前为止,我只在我的私人测试工具中使用它。但是与其他解决方案(如使用Jansi的JLine2)相比,源代码相对较小,因此出错的可能性不大。我编写它是因为JLine2不支持无阻塞的单字符输入。 - Christian d'Heureuse
这就是我所说的压力测试——可能用词不当,我的错。不管怎样,太棒了!我在我的一个学校项目中借鉴了它,并且帮了我很多。 - anon
嗨 - 这个类看起来很棒。但是:我无法让它工作...我应该如何使用它?我遇到了 System.in 阻塞的问题,直到我按下 CTRL+D(在 Linux 上),现在我读到了关于控制台模式之类的内容。我认为你的 RawConsoleInput 就是我要找的东西 - 但我该如何使用它呢? - Igor
1
@M.E. 需要在RawConsoleInput.java之外添加JNA jar。此外,JNA在版本5中已经废弃了Pointer.SIZE(https://github.com/kaitoy/pcap4j/issues/191),因此我们还需要将第170行替换为`Native.POINTER_SIZE`。 - WaterGenie
显示剩余6条评论

18

没有一种便携方式可以从Java控制台读取原始字符。

上面提供了一些平台相关的解决方法。但要真正实现可移植性,您需要放弃控制台模式并使用窗口模式,例如AWT或Swing。


14

请使用jline3

示例:

Terminal terminal = TerminalBuilder.builder()
    .jna(true)
    .system(true)
    .build();

// raw mode means we get keypresses rather than line buffered input
terminal.enterRawMode();
reader = terminal .reader();
...
int read = reader.read();
....
reader.close();
terminal.close();

我发现基于RawConsoleInput的解决方案在MacOS High Sierra上不起作用;然而,这个解决方案完美地工作。 - RawToast
jline几乎拥有您创建交互式控制台/终端系统所需的一切。它在Linux中运行良好。要获取更完整的示例,请查看:https://github.com/jline/jline3/blob/master/builtins/src/test/java/org/jline/example/Example.java。它具有自动完成、历史记录、密码掩码等功能。 - lepe

1
我已经使用jcurses完成了它...
import jcurses.system.InputChar;
import jcurses.system.Toolkit;

//(works best on the local machine when run through screen)
public class readchar3 {
    public static void main (String[] args)
        {
            String st;
            char ch;
            int i;
            st = "";
            ch = ' ';
            i = 0;
            while (true)
                {
                        InputChar c = Toolkit.readCharacter();
                    ch = c.getCharacter();
                    i = (int) ch;
                    System.out.print ("you typed " + ch + "(" + i + ")\n\r");
                    // break on '#'
                    if (ch == '#') break;
                }
            System.out.println ("Programm wird beendet. Verarbeitung kann beginnen.");
        }
}

0

看这个

它调用了 C 语言中的 _getch() 函数,可以读取单个字符而无需按下回车键


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