Comment lire un seul caractère de la console en Java (au fur et à mesure que l'utilisateur le tape)?

110

Existe-t-il un moyen simple de lire un seul caractère de la console lorsque l'utilisateur le saisit en Java? C'est possible? J'ai essayé ces méthodes, mais elles attendent toutes que l'utilisateur appuie sur la touche Entrée :

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

Je commence à penser que System.in n'est pas conscient de l'entrée de l'utilisateur jusqu'à ce que vous appuyez sur Entrée .

Victor Hugo
la source

Réponses:

57

Ce que vous voulez faire est de mettre la console en mode «brut» (édition de ligne contournée et aucune touche d'entrée requise) par opposition au mode «cuit» (édition de ligne avec touche d'entrée requise.) Sur les systèmes UNIX, la commande «stty» peut changer de mode.

Maintenant, en ce qui concerne Java ... voir Entrée de console non bloquante en Python et Java . Extrait:

Si votre programme doit être basé sur une console, vous devez passer votre terminal du mode ligne en mode caractère et n'oubliez pas de le restaurer avant que votre programme ne se ferme. Il n'existe aucun moyen portable de le faire sur tous les systèmes d'exploitation.

L'une des suggestions est d'utiliser JNI. Encore une fois, ce n'est pas très portable. Une autre suggestion à la fin du fil de discussion, et en commun avec l'article ci-dessus, consiste à utiliser jCurses .

Chris W. Rea
la source
4
JCurses n'est pas non plus très portable .... D'après le fichier README de JCurses: "JCurses se compose de deux parties: la partie indépendante de la plate-forme et la partie dépendante de la plate-forme, qui consiste en une bibliothèque partagée native rendant les opérations d'entrée et de sortie primitives disponibles pour la première partie . "
Ryan Fernandes
6
@RyanFernandes me semble assez portable - un seul outil qui peut être exécuté sur plusieurs systèmes (en utilisant différentes dépendances)
Antoniossss
25

Vous devez faire passer votre console en mode brut. Il n'y a pas de moyen intégré indépendant de la plate-forme pour y arriver. jCurses pourrait être intéressant, cependant.

Sur un système Unix, cela peut fonctionner:

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

Par exemple, si vous souhaitez prendre en compte le temps entre les frappes, voici un exemple de code pour y arriver.

nda1983
la source
A bien fonctionné pour moi sous Linux
MrSmith42
4
A également travaillé sur Mac. Vous voudrez probablement mentionner qu'il stty cooked </dev/ttydoit être exécuté lorsque le programme doit revenir en mode tampon, et définitivement avant la fermeture du programme.
Kelvin
15

Il n'existe aucun moyen portable de lire des caractères bruts à partir d'une console Java.

Certaines solutions de contournement dépendant de la plate-forme ont été présentées ci-dessus. Mais pour être vraiment portable, il faudrait abandonner le mode console et utiliser un mode fenêtrage, par exemple AWT ou Swing.

rustyx
la source
22
Je ne comprends pas très bien pourquoi, par exemple, Mono (ou CLR) a le System.Console.ReadKeyqui fonctionne sur toutes les plates-formes. Java distribue également JVM et JRE pour chaque plate-forme avec des bibliothèques et des implémentations dépendant de la plate-forme, ce n'est donc pas une excuse.
Martin Macak
13

J'ai écrit une classe Java RawConsoleInput qui utilise JNA pour appeler les fonctions du système d'exploitation de Windows et Unix / Linux.

  • Sous Windows, il utilise _kbhit()et à _getwch()partir de msvcrt.dll.
  • Sous Unix, il utilise tcsetattr()pour basculer la console en mode non canonique, System.in.available()pour vérifier si les données sont disponibles et System.in.read()pour lire les octets de la console. A CharsetDecoderest utilisé pour convertir des octets en caractères.

Il prend en charge l'entrée non bloquante et mélange le mode brut et l'entrée en mode ligne normale.

Christian d'Heureuse
la source
Dans quelle mesure cela a-t-il été testé / soumis à des tests de résistance?
Fund Monica's Lawsuit
2
@QPaysTaxes Les tests de résistance sont difficiles pour l'entrée de la console. Je pense que dans ce cas, il serait plus important de le tester dans différents environnements (différentes versions de Windows / Linux, 64/32 bits, Linux via SSH, Telnet, port série ou console de bureau, etc.). Jusqu'à présent, je ne l'utilise que dans mes outils de test privés. Mais le code source est relativement petit, comparé à d'autres solutions (comme JLine2 qui utilise Jansi). Il n'y a donc pas grand-chose qui puisse mal tourner. Je l'ai écrit, car JLine2 ne prend pas en charge la saisie d'un seul caractère sans blocage.
Christian d'Heureuse
C'est ce que je voulais dire par test de résistance - c'est probablement le mauvais mot; ma faute. Bref, sympa! Je l'ai volé ^ H ^ H ^ H ^ H ^ H ^ Je l'ai utilisé dans un de mes projets pour l'école et cela a aidé un groupe.
Fund Monica's Lawsuit
Hé - ce cours a l'air génial. Cependant: je n'arrive pas à le faire fonctionner .. comment dois-je l'utiliser? J'ai rencontré le blocage de System.in jusqu'à ce que j'appuie sur CTRL + D (sous Linux) et maintenant je lis sur les modes de la console et autres. Je pense que votre RawConsoleInput est ce que je recherche - mais comment l'utiliser?
Igor
@Igor Appelez simplement RawConsoleInput.read (booléen) pour lire un caractère du clavier. Il est documenté dans le code source (RawConsoleInput.java).
Christian d'Heureuse
8

Utilisez jline3 :

Exemple:

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();
Cosse
la source
J'ai trouvé que les solutions basées sur RawConsoleInput ne fonctionnaient pas sur MacOS High Sierra; cependant, cela fonctionne parfaitement.
RawToast
jline a pratiquement tout ce dont vous avez besoin pour créer un système de console / terminal interactif. Cela fonctionne très bien sous Linux. Pour un exemple plus complet, consultez: github.com/jline/jline3/blob/master/builtins/src/test/java/org/… . Il a la saisie semi-automatique, l'historique, le masque de mot de passe, etc.
lepe