Como ler um único caractere do console em Java (conforme o usuário o digita)?

110

Existe uma maneira fácil de ler um único caractere no console enquanto o usuário o digita em Java? É possível? Eu tentei com esses métodos, mas todos eles esperam que o usuário pressione Enter tecla :

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

Estou começando a pensar que System.in não está ciente da entrada do usuário até que enter seja pressionado.

Victor Hugo
fonte

Respostas:

57

O que você deseja fazer é colocar o console no modo "bruto" (edição de linha ignorada e nenhuma tecla Enter necessária) em oposição ao modo "cozido" (edição de linha com a tecla Enter necessária.) Em sistemas UNIX, o comando 'stty' pode mudar os modos.

Agora, com relação ao Java ... consulte Entrada do console sem bloqueio em Python e Java . Excerto:

Se seu programa deve ser baseado em console, você tem que mudar seu terminal do modo de linha para o modo de caractere e lembrar de restaurá-lo antes de seu programa encerrar. Não existe uma maneira portátil de fazer isso em sistemas operacionais.

Uma das sugestões é usar JNI. Novamente, isso não é muito portátil. Outra sugestão no final do tópico, e em comum com a postagem acima, é examinar o uso de jCurses .

Chris W. Rea
fonte
4
JCurses também não é muito portátil .... Do README do JCurses: "JCurses consiste em duas partes: a parte independente da plataforma e a parte dependente da plataforma, que consiste em uma biblioteca compartilhada nativa que disponibiliza operações primitivas de entrada e saída para a primeira parte . "
Ryan Fernandes
6
@RyanFernandes parece bastante portátil para mim - ferramenta única que pode ser executada em vários sistemas (usando dependências diferentes)
Antoniossss
25

Você precisa colocar seu console em modo bruto. Não existe uma maneira independente de plataforma integrada de chegar lá. jCurses pode ser interessante, no entanto.

Em um sistema Unix, isso pode funcionar:

String[] cmd = {"/bin/sh", "-c", "stty raw </dev/tty"};
Runtime.getRuntime().exec(cmd).waitFor();

Por exemplo, se você quiser levar em consideração o tempo entre os pressionamentos de tecla, aqui está o código de exemplo para chegar lá.

nes1983
fonte
Funcionou bem para mim no Linux
MrSmith42
4
Funcionou no Mac também. Você provavelmente deseja mencionar que stty cooked </dev/ttydeve ser executado quando o programa precisar reverter para o modo de buffer e, definitivamente, antes de encerrar o programa.
Kelvin
15

Não há uma maneira portátil de ler caracteres brutos de um console Java.

Algumas soluções alternativas dependentes da plataforma foram apresentadas acima. Mas para ser realmente portátil, você teria que abandonar o modo console e usar um modo de janela, por exemplo, AWT ou Swing.

enferrujado
fonte
22
Não entendo muito bem por que, por exemplo, o Mono (ou CLR) tem o System.Console.ReadKeyque funciona em todas as plataformas. Java também distribui JVM e JRE para todas as plataformas com bibliotecas e implementações dependentes da plataforma, então isso não é desculpa.
Martin Macak
13

Eu escrevi uma classe Java RawConsoleInput que usa JNA para chamar funções do sistema operacional do Windows e Unix / Linux.

  • No Windows ele usa _kbhit()e _getwch()de msvcrt.dll.
  • No Unix, ele usa tcsetattr()para mudar o console para o modo não canônico, System.in.available()para verificar se os dados estão disponíveis e System.in.read()para ler bytes do console. A CharsetDecoderé usado para converter bytes em caracteres.

Ele suporta entrada sem bloqueio e modo de mixagem bruto e entrada de modo de linha normal.

Christian d'Heureuse
fonte
Com que intensidade isso foi testado / testado contra estresse?
Ação judicial de Monica de
2
@QPaysTaxes O teste de estresse é difícil para a entrada do console. Acho que, neste caso, seria mais importante testá-lo em vários ambientes (diferentes versões do Windows / Linux, 64/32 bits, Linux via SSH, Telnet, porta serial ou console de desktop, etc.). Até agora, só o uso em minhas ferramentas de teste privadas. Mas o código-fonte é relativamente pequeno, em comparação com outras soluções (como JLine2 que usa Jansi). Portanto, não há muito que possa dar errado. Eu o escrevi, porque JLine2 não suporta a entrada de um único caractere sem bloqueio.
Christian d'Heureuse
Isso é o que eu quis dizer com teste de estresse - provavelmente é a palavra errada; foi mal. Enfim, legal! Eu roubei ^ H ^ H ^ H ^ H ^ H ^ Eu o usei em um projeto meu para a escola e isso ajudou muito.
Processo de Fundo Monica de
Ei - esta aula parece ótima. No entanto: não consigo fazer funcionar .. como devo usá-lo? Encontrei o bloqueio de System.in até pressionar CTRL + D (no Linux) e agora li sobre os modos de console e similares. Acho que seu RawConsoleInput é o que estou procurando - mas como faço para usá-lo?
Igor
@Igor Basta chamar RawConsoleInput.read (boolean) para ler um caractere do teclado. Está documentado no código-fonte (RawConsoleInput.java).
Christian d'Heureuse de
8

Use jline3 :

Exemplo:

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();
Pod
fonte
Descobri que as soluções baseadas em RawConsoleInput não funcionavam no MacOS High Sierra; no entanto, isso funciona perfeitamente.
RawToast de
jline tem praticamente tudo que você precisa para criar um sistema de console / terminal interativo. Funciona muito bem no Linux. Para um exemplo mais completo, veja: github.com/jline/jline3/blob/master/builtins/src/test/java/org/… . Possui preenchimento automático, histórico, máscara de senha, etc.
lepe