Wie lese ich ein einzelnes Zeichen von der Konsole in Java (während der Benutzer es eingibt)?

110

Gibt es eine einfache Möglichkeit, ein einzelnes Zeichen von der Konsole zu lesen, während der Benutzer es in Java eingibt? Ist es möglich? Ich habe es mit diesen Methoden versucht, aber alle warten darauf, dass der Benutzer die Eingabetaste drückt :

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

Ich fange an zu glauben , dass System.in keine Kenntnis von der Benutzereingabe , bis Eingabe gedrückt wird.

Victor Hugo
quelle

Antworten:

57

Sie möchten die Konsole in den "Raw" -Modus versetzen (Zeilenbearbeitung umgangen und keine Eingabetaste erforderlich) und nicht in den "gekochten" Modus (Zeilenbearbeitung mit Eingabetaste erforderlich). Auf UNIX-Systemen kann der Befehl 'stty' verwendet werden Modi wechseln.

In Bezug auf Java ... siehe Nicht blockierende Konsoleneingabe in Python und Java . Auszug:

Wenn Ihr Programm konsolenbasiert sein muss, müssen Sie Ihr Terminal aus dem Zeilenmodus in den Zeichenmodus schalten und daran denken, es wiederherzustellen, bevor Ihr Programm beendet wird. Es gibt keine tragbare Möglichkeit, dies betriebssystemübergreifend zu tun.

Einer der Vorschläge ist die Verwendung von JNI. Auch das ist nicht sehr portabel. Ein weiterer Vorschlag am Ende des Threads und gemeinsam mit dem obigen Beitrag ist die Verwendung von jCurses .

Chris W. Rea
quelle
4
JCurses ist auch nicht sehr portabel ... Aus der JCurses-README: "JCurses besteht aus zwei Teilen: dem plattformunabhängigen Teil und dem plattformabhängigen Teil, der aus einer nativen gemeinsam genutzten Bibliothek besteht, die primitive Eingabe- und Ausgabeoperationen für den ersten Teil verfügbar macht . "
Ryan Fernandes
6
@ RyanFernandes klingt für mich ziemlich portabel - ein einziges Tool, das auf mehreren Systemen (mit unterschiedlichen Abhängigkeiten) ausgeführt werden kann
Antoniossss
25

Sie müssen Ihre Konsole in den Raw-Modus versetzen. Es gibt keine integrierte plattformunabhängige Möglichkeit, dorthin zu gelangen. jCurses könnte jedoch interessant sein.

Auf einem Unix-System könnte dies funktionieren:

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

Wenn Sie beispielsweise die Zeit zwischen den Tastenanschlägen berücksichtigen möchten, finden Sie hier einen Beispielcode, um dorthin zu gelangen.

nes1983
quelle
Hat
4
Funktionierte auch auf dem Mac. Sie möchten wahrscheinlich erwähnen, dass dies ausgeführt werden stty cooked </dev/ttysollte, wenn das Programm in den gepufferten Modus zurückkehren muss und definitiv bevor das Programm beendet wird.
Kelvin
15

Es gibt keine tragbare Möglichkeit, Rohzeichen von einer Java-Konsole zu lesen.

Einige plattformabhängige Problemumgehungen wurden oben vorgestellt. Aber um wirklich portabel zu sein, müssten Sie den Konsolenmodus verlassen und einen Fenstermodus verwenden, z. B. AWT oder Swing.

Rustyx
quelle
22
Ich verstehe nicht ganz, warum zum Beispiel Mono (oder CLR) das hat, System.Console.ReadKeywas auf allen Plattformen funktioniert. Java verteilt auch JVM und JRE für jede Plattform mit plattformabhängigen Bibliotheken und Implementierungen. Das ist also keine Entschuldigung.
Martin Macak
13

Ich habe eine Java-Klasse RawConsoleInput geschrieben , die JNA verwendet , um Betriebssystemfunktionen von Windows und Unix / Linux aufzurufen.

  • Unter Windows wird _kbhit()und _getwch()von msvcrt.dll verwendet.
  • Unter Unix wird tcsetattr()die Konsole in den nicht-kanonischen Modus geschaltet, um System.in.available()zu überprüfen, ob Daten verfügbar sind, und System.in.read()um Bytes von der Konsole zu lesen. A CharsetDecoderwird verwendet, um Bytes in Zeichen umzuwandeln.

Es unterstützt den nicht blockierenden Eingang und das Mischen des Raw-Modus und des normalen Line-Modus-Eingangs.

Christian d'Heureuse
quelle
Wie stark wurde dies getestet / auf Stress getestet?
Fund Monica Klage
2
@QPaysTaxes Stresstests sind für die Konsoleneingabe schwierig. Ich denke, in diesem Fall wäre es wichtiger, es in verschiedenen Umgebungen zu testen (verschiedene Windows / Linux-Versionen, 64/32 Bit, Linux über SSH, Telnet, serielle Schnittstelle oder Desktop-Konsole usw.). Bisher verwende ich es nur in meinen privaten Testwerkzeugen. Der Quellcode ist jedoch im Vergleich zu anderen Lösungen (wie JLine2, das Jansi verwendet) relativ klein. Es kann also nicht viel schief gehen. Ich habe es geschrieben, weil JLine2 die Eingabe einzelner Zeichen ohne Blockierung nicht unterstützt.
Christian d'Heureuse
Das habe ich mit Stresstest gemeint - es ist wahrscheinlich das falsche Wort; mein Fehler. Wie auch immer, schön! Ich habe ^ H ^ H ^ H ^ H ^ H ^ gestohlen. Ich habe es in einem meiner Projekte für die Schule verwendet und es hat einem Haufen geholfen.
Fund Monica Klage
Hey - diese Klasse sieht toll aus. Allerdings: Ich kann es nicht zum Laufen bringen. Wie soll ich es verwenden? Ich habe festgestellt, dass System.in blockiert, bis ich STRG + D (unter Linux) drücke, und jetzt lese ich über Konsolenmodi und dergleichen. Ich denke, Ihr RawConsoleInput ist das, wonach ich suche - aber wie verwende ich ihn?
Igor
@Igor Rufen Sie einfach RawConsoleInput.read (boolean) auf, um ein Tastaturzeichen zu lesen. Es ist im Quellcode (RawConsoleInput.java) dokumentiert.
Christian d'Heureuse
8

Verwenden Sie jline3 :

Beispiel:

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
quelle
Ich habe festgestellt, dass RawConsoleInput-basierte Lösungen unter MacOS High Sierra nicht funktionieren. Dies funktioniert jedoch perfekt.
RawToast
jline bietet praktisch alles, was Sie zum Erstellen eines interaktiven Konsolen- / Terminalsystems benötigen. Es funktioniert gut unter Linux. Ein vollständigeres Beispiel finden Sie unter: github.com/jline/jline3/blob/master/builtins/src/test/java/org/… . Es hat Autocomplete, Verlauf, Passwortmaske, etc.
Lepe