Как прочитать один символ из консоли в Java (по мере его ввода пользователем)?

110

Есть ли простой способ прочитать один символ из консоли, когда пользователь вводит его на Java? Является ли это возможным? Я пробовал использовать эти методы, но все они ждут, пока пользователь нажмет клавишу ввода :

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 не знает ввода пользователя, пока не будет нажата клавиша Enter .

Виктор Гюго
источник

Ответы:

57

Что вы хотите сделать, так это перевести консоль в "сырой" режим (строковое редактирование не требуется и клавиша ввода не требуется) в отличие от "приготовленного" режима (требуется редактирование строки с помощью клавиши ввода). В системах UNIX команда 'stty' может изменить режимы.

Теперь, что касается Java ... см. Неблокирующий ввод консоли в Python и Java . Отрывок:

Если ваша программа должна быть консольной, вы должны переключить терминал из линейного режима в символьный и не забыть восстановить его перед завершением программы. Нет переносимого способа сделать это в разных операционных системах.

Одно из предложений - использовать JNI. Опять же, это не очень удобно. Еще одно предложение в конце обсуждения, общее с сообщением выше, - изучить использование jCurses .

Крис В. Ри
источник
4
JCurses также не очень портативен ... Из README JCurses: «JCurses состоит из двух частей: независимой от платформы части и части, зависимой от платформы, которая состоит из собственной общей библиотеки, делающей примитивные операции ввода и вывода доступными для первой части. . "
Райан Фернандес,
6
@RyanFernandes звучит для меня довольно портативно - единый инструмент, который можно запускать в нескольких системах (с использованием разных зависимостей)
Антониосс,
25

Вам нужно вывести консоль в необработанный режим. Нет встроенного независимого от платформы способа добраться туда. Хотя jCurses может быть интересным.

В системе Unix это может сработать:

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

Например, если вы хотите учитывать время между нажатиями клавиш, вот пример кода для этого.

Nes1983
источник
У меня отлично
работало
4
Работал и на Mac. Вы, вероятно, захотите упомянуть, что это stty cooked </dev/ttyследует запускать, когда программе необходимо вернуться в буферный режим, и обязательно до выхода из программы.
Кельвин
15

Не существует переносимого способа чтения необработанных символов с консоли Java.

Некоторые обходные пути, зависящие от платформы, были представлены выше. Но чтобы быть действительно портативным, вам нужно отказаться от консольного режима и использовать оконный режим, например AWT или Swing.

ржавый
источник
22
Я не совсем понимаю, почему, например, Mono (или CLR) System.Console.ReadKeyработает на всех платформах. Java также распространяет JVM и JRE для каждой платформы с зависимыми от платформы библиотеками и реализациями, так что это не оправдание.
Мартин Макак,
13

Я написал Java-класс RawConsoleInput, который использует JNA для вызова функций операционной системы Windows и Unix / Linux.

  • В Windows используется _kbhit()и _getwch()из msvcrt.dll.
  • В Unix он используется tcsetattr()для переключения консоли в неканонический режим, System.in.available()для проверки наличия данных и System.in.read()для чтения байтов с консоли. A CharsetDecoderиспользуется для преобразования байтов в символы.

Он поддерживает неблокирующий ввод и режим микширования необработанного и нормального линейного режима ввода.

Кристиан д'Эрёз
источник
Насколько тщательно это было протестировано / подверглось стресс-тестированию?
Иск Фонда Моники
2
@QPaysTaxes Стресс-тестирование сложно вводить с консоли. Думаю, в этом случае важнее было бы протестировать его в различных средах (разные версии Windows / Linux, 64/32 бит, Linux через SSH, Telnet, последовательный порт или настольную консоль и т. Д.). Пока я использую его только в своих частных тестовых инструментах. Но исходный код относительно невелик по сравнению с другими решениями (например, JLine2, который использует Jansi). Так что мало что может пойти не так. Я написал это, потому что JLine2 не поддерживает ввод одиночных символов без блокировки.
Christian d'Heureuse
Вот что я имел в виду под стресс-тестом - наверное, неправильное слово; моя вина. В любом случае приятно! Я украл ^ H ^ H ^ H ^ H ^ H ^ Я использовал его в своем школьном проекте, и это очень помогло.
Иск Фонда Моники
Эй, этот класс выглядит великолепно. Однако: я не могу заставить его работать .. как я должен его использовать? Я столкнулся с блокировкой System.in, пока не нажал CTRL + D (в Linux), и теперь я читаю о консольных режимах и тому подобном. Я думаю, что ваш RawConsoleInput - это то, что я ищу, но как мне его использовать?
Игорь
@Igor Просто вызовите RawConsoleInput.read (boolean), чтобы прочитать символ клавиатуры. Это задокументировано в исходном коде (RawConsoleInput.java).
Christian d'Heureuse
8

Используйте 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. Для более полного примера посмотрите: github.com/jline/jline3/blob/master/builtins/src/test/java/org/… . В нем есть автозаполнение, история, маска пароля и т. Д.
lepe