Как избежать нажатия Enter с помощью getchar () для чтения только одного символа?

80

В следующем коде:

Мне нужно нажать, Enterчтобы напечатать все буквы, которые я ввел getchar, но я не хочу этого делать, я хочу нажать на букву и сразу увидеть введенную мной букву, повторяющуюся без нажатия Enter. Например, если я нажимаю букву «а», я хочу видеть рядом с ней другую букву «а» и так далее:

Но когда я нажимаю «а», ничего не происходит, я могу писать другие буквы, и копия появляется только тогда, когда я нажимаю Enter:

Как я могу это сделать?

Я использую команду cc -o example example.cпод Ubuntu для компиляции.

Хавьер
источник
5
это очень зависит от того, как ваша ОС и терминальный процесс вводят. если вы укажете свою операционную среду, вы, вероятно, получите лучшие ответы.
goldPseudo
Ответ, представленный на cplusplus.com/forum/articles/19975, работает в Windows с использованием WinAPI.

Ответы:

72

В системе Linux вы можете изменить поведение терминала с помощью sttyкоманды. По умолчанию терминал буферизует всю информацию, пока она не Enterбудет нажата, прежде чем даже отправить ее в программу C.

Быстрый, грязный и не особо переносимый пример изменения поведения из самой программы:

Обратите внимание, что это не совсем оптимальный вариант, поскольку он просто предполагает, что stty cookedименно такое поведение вы хотите при выходе из программы, а не проверяет, какими были исходные настройки терминала. Кроме того, поскольку в необработанном режиме вся специальная обработка пропускается, многие последовательности клавиш (такие как CTRL-C или CTRL-D ) фактически не будут работать так, как вы ожидаете, без явной обработки их в программе.

Вы можете получить man sttyбольший контроль над поведением терминала, в зависимости от того, чего вы хотите достичь.

goldПсевдо
источник
Использование system- плохая идея.
Sapphire_Brick
Слишком много сырых ... Я искал решение, которое позволило бы мне не ждать ENTER. Это решение нарушает все функции входных клемм.
ABC
93

Это зависит от вашей ОС, если вы находитесь в среде UNIX, флаг ICANON включен по умолчанию, поэтому ввод буферизируется до следующего '\n'или EOF. Отключив канонический режим, вы сразу получите персонажей. Это также возможно на других платформах, но прямого кроссплатформенного решения не существует.

РЕДАКТИРОВАТЬ: Я вижу, вы указали, что используете Ubuntu. Я только что опубликовал нечто подобное вчера, но имейте в виду, что это отключит многие стандартные поведения вашего терминала.

Вы заметите, что каждый персонаж появляется дважды. Это связано с тем, что ввод немедленно передается обратно на терминал, а затем ваша программа putchar()тоже возвращает его . Если вы хотите отделить вход от выхода, вам также необходимо отключить флаг ECHO. Вы можете сделать это, просто изменив соответствующую строку на:

Лукас
источник
9
+1 Хотя tbh, вероятно, это не тот уровень кода, который устраивает оригинальный постер;)
Andomar
2
Большой! Вот что я искал!
Denilson Sá Maia
Спасибо за помощь вместе с подробными комментариями, это решило мою проблему.
kaminsknator
Невероятно, что что-то столь простое так сложно реализовать и не переносимо.
Pedro77
12

getchar () - это стандартная функция, которая на многих платформах требует, чтобы вы нажали ENTER, чтобы получить ввод, потому что платформа буферизует ввод, пока не будет нажата эта клавиша. Многие компиляторы / платформы поддерживают нестандартную функцию getch (), которая не заботится о ENTER (обходит буферизацию платформы, обрабатывает ENTER как просто еще одну клавишу).

Эрик Дж.
источник
1
+1 Правильно, getchar()начинает читать символы один за другим только после того, как вы нажали ввод
Andomar
32
getchar()не заботится о ENTER, он просто обрабатывает все, что приходит через stdin. Буферизация строки обычно определяется операционной системой / терминалом.
goldPseudo
1
@goldPseudo: Верно, но почти все платформы буферизуют ввод, так что это поведение, которое вы увидите в большинстве практических случаев. getch (), если он поддерживается, либо управляет, либо игнорирует буферизацию. Я знаю, что некоторые старые реализации DOS обходили буферизацию ОС, чтобы напрямую взаимодействовать с оборудованием. Другие реализации могут сбрасывать стандартный ввод для получения того же поведения.
Эрик Дж.
10
Тем не менее, это не getchar()«требует нажатия клавиши ввода», а среда.
Benji XVI
1
Очень кратко и легко для понимания. Другие ответы с самым высоким рейтингом просто содержат массу ненужных технических деталей.
mzoz
6

Ввод-вывод - это функция операционной системы. Во многих случаях операционная система не передает набранный символ программе, пока не будет нажата клавиша ENTER. Это позволяет пользователю изменять ввод (например, ввод назад и повторный ввод) перед его отправкой в ​​программу. В большинстве случаев это работает хорошо, предоставляет пользователю единообразный интерфейс и избавляет программу от этого. В некоторых случаях желательно, чтобы программа получала символы с клавиш по мере их нажатия.

Сама библиотека C работает с файлами и не заботится о том, как данные попадают во входной файл. Следовательно, в самом языке нет возможности получать клавиши по мере их нажатия; вместо этого это зависит от платформы. Поскольку вы не указали ОС или компилятор, мы не можем найти его для вас.

Кроме того, стандартный вывод обычно буферизуется для повышения эффективности. Это делается библиотеками C, поэтому существует решение C, которое следует fflush(stdout);после каждого написанного символа. После этого, будут ли символы отображаться немедленно, зависит от операционной системы, но все операционные системы, с которыми я знаком, будут отображать вывод немедленно, так что обычно это не проблема.

Дэвид Торнли
источник
6

Мне нравится ответ Лукаса, но я хотел бы его немного уточнить. В termios.hnamed есть встроенная функция, cfmakeraw()которую человек описывает как:

Это в основном делает то же самое, что предлагал Лукас, и более того, вы можете увидеть точные флаги, которые он устанавливает на страницах руководства: termios (3) .

Пример использования

свет
источник
Обратите внимание, что cfmakeraw()это непереносимое нестандартное расширение POSIX,termios.h которое не обязательно доступно.
Эндрю Хенле
4

Поскольку вы работаете над производной Unix (Ubuntu), вот один из способов сделать это - не рекомендуется, но он будет работать (если вы можете точно вводить команды):

Используйте прерывание, чтобы остановить программу, когда она вам наскучила.

  • Строка 'echo' сохраняет текущие настройки терминала в виде сценария оболочки, который их восстановит.
  • Строка 'stty' отключает большую часть специальной обработки (например Control-D, не имеет никакого эффекта) и отправляет символы в программу, как только они становятся доступными. Это означает, что вы больше не можете редактировать свой ввод.
  • Строка 'sh' восстанавливает исходные настройки терминала.

Вы можете сэкономить, если stty sane восстанавливает ваши настройки достаточно точно для ваших целей. Формат '-g' не переносится между версиями 'stty' (так что то, что сгенерировано в Solaris 10, не будет работать в Linux или наоборот), но концепция работает везде. Параметр 'stty sane' доступен не везде, AFAIK (но есть в Linux).

Джонатан Леффлер
источник
2

Вы можете включить библиотеку ncurses и использовать getch()вместо getchar().

Ашелли
источник
1

да, вы можете сделать это и в Windows, вот код ниже, используя библиотеку conio.h

djaa2807
источник
6
Вопрос помечен c , а не c ++
Spikatrix
@CoolGuy И что? Зачем кому-то помочь, если кто-то открыл точно такой же вопрос, но изменил тег с c на c ++?
Андреас Хафербург
@AndreasHaferburg Потому что здесь у нас не было бы ответов на C ++, особенно плохо написанных, подмигнуть .
Sapphire_Brick
1

У меня возникла эта проблема / вопрос в задании, над которым я сейчас работаю. Это также зависит от того, какой ввод вы берете. Я использую

чтобы получить ввод во время работы программы, так что это должен быть файловый поток, связанный с командой.

На машине ubuntu я должен тестировать / таргетировать, это требовало большего, чем просто

или же

Мне пришлось добавить флаг --file, а также путь к команде, например:

Теперь все копацетно.

Чад Шабо
источник
2
обратите внимание, что это решение зависит от ОС.
Оттавио Кампана,
0

Этот код работал у меня. Внимание: это не часть стандартной библиотеки, даже если большинство компиляторов (я использую GCC) поддерживает ее.

Этот код обнаруживает первое нажатие клавиши.

Firefnix
источник
0

" Как избежать нажатия Enterс помощью getchar()? "

Прежде всего, вход терминала обычно либо линейный, либо полностью буферизованный. Это означает, что операционная система сохраняет фактический ввод с терминала в буфер. Обычно этот буфер сбрасывается в программу, когда fe \nбыл сигнализирован / предоставлен в stdin. Это делается с помощью пресса Enter.

getchar()находится как раз в конце цепочки. У него нет возможности реально влиять на процесс буферизации.


" Как я могу это сделать? "

Ditchgetchar() в первую очередь, если вы не хотите использовать определенные системные вызовы , чтобы изменить поведение терминала явно , как хорошо объяснено в других ответах.

К сожалению, нет стандартной библиотечной функции и, следовательно, нет переносимого способа очистки буфера при вводе одного символа. Однако есть решения на основе реализации и непереносимые.


В Windows / MS-DOS, Есть getch()и getche()функции в conio.hфайле заголовка, которые делают точно , что вы хотите - читать один символ без необходимости ожидания для перехода на новую строку , чтобы очистить буфер.

Основное различие между getch()и getche()заключается в том getch(), что фактический входной символ не выводится сразу в консоль, в то время как это getche()делается. Дополнительные "e"расшифровываются как эхо .

Пример:


В Linux, таким образом , чтобы получить непосредственную обработку и вывод символов заключается в использовании cbreak()и echo()вариантов и getch()и refresh()подпрограмм в Ncurses-библиотеке.

Обратите внимание, что вам необходимо инициализировать так называемый стандартный экран с помощью initscr()и закрыть то же самое с endwin()подпрограммами.

Пример:


Примечание: вам нужно вызвать компилятор с -lncursesопцией, чтобы компоновщик мог искать и находить библиотеку ncurses.

RobertS поддерживает Монику Челлио
источник
-1

По умолчанию библиотека C буферизует вывод, пока не увидит возврат. Чтобы сразу распечатать результаты, используйте fflush:

Андомар
источник
2
Однако вывод - это часть проблемы. Запрашивающий хочет, чтобы программа считывала клавиши при их нажатии и немедленно печатала на экране. Это проблема как ввода, так и вывода.
Дэвид Торнли
т.е. fflush только сбрасывает вывод, но getchar все равно не вернется после нажатия клавиши (клавиши будут буферизированы).
NickSoft