Як уникнути натискання клавіші Enter за допомогою getchar () для читання лише одного символу?


80

У наступному коді:

Я повинен натиснути, Enterщоб надрукувати всі введені літери getchar, але я не хочу цього робити, я хочу натиснути на лист і негайно побачити лист, який я ввів, повторений без натискання Enter. Наприклад, якщо я натискаю букву "а", я хочу побачити поруч іншу "а" тощо:

Але коли я натискаю "a", нічого не відбувається, я можу писати інші літери, і копія з'являється лише тоді, коли я натискаю Enter:

Як я можу це зробити?

Я використовую команду cc -o example example.cпід Ubuntu для компіляції.


5
це дуже залежить від того, як ваша ОС та термінал обробляють дані. якщо ви вкажете своє робоче середовище, ви, мабуть, отримаєте кращі відповіді.
goldPseudo

Відповідь, надана на cplusplus.com/forum/articles/19975, працює на вікнах за допомогою WinAPI.

Відповіді:


72

У системі Linux ви можете змінити поведінку терміналу за допомогою sttyкоманди. За замовчуванням термінал буферизує всю інформацію, доки не Enterбуде натиснуто, ще до того, як відправити її програмі C.

Швидкий, брудний і не особливо портативний приклад зміни поведінки всередині самої програми:

Зверніть увагу, що це насправді не є оптимальним, оскільки це просто начебто передбачає, що stty cookedсаме така поведінка вам потрібна під час виходу програми, а не перевірка вихідних налаштувань терміналу. Крім того, оскільки вся спеціальна обробка пропускається в необробленому режимі, багато послідовностей ключів (наприклад, CTRL-C або CTRL-D ) насправді не працюватимуть, як ви очікуєте, без явної обробки їх у програмі.

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


Використання system- погана ідея.
Sapphire_Brick

Занадто багато сировини .... Я шукав рішення, яке дозволило б мені не чекати ENTER. Це рішення гальмує всі функції вхідного терміналу.
ABC

93

Це залежить від вашої ОС, якщо ви перебуваєте в середовищі, подібному до UNIX, прапорець ICANON увімкнено за замовчуванням, тому введення буферизується до наступного '\n'або EOF. Вимкнувши канонічний режим, ви одразу отримаєте персонажів. Це можливо і на інших платформах, але прямого крос-платформного рішення немає.

EDIT: Бачу, ви вказали, що використовуєте Ubuntu. Я щойно опублікував щось подібне, але майте на увазі, що це вимкне багато типів поведінки вашого терміналу за замовчуванням.

Ви помітите, що кожен персонаж з'являється двічі. Це пов’язано з тим, що вхід негайно повертається назад до терміналу, а потім програма також повертає його назад putchar(). Якщо ви хочете від'єднати вхід від виводу, вам також доведеться повернути прапор ECHO. Ви можете зробити це, просто змінивши відповідний рядок на:


9
+1 Хоча tbh, це, мабуть, не той рівень коду, з яким спокійний оригінальний плакат;)
Andomar

2
Чудово! Це те, що я шукав!
Denilson Sá Maia

Дякуємо за допомогу разом із докладними коментарями, це вирішило мою проблему.
kaminsknator

Неймовірно, що чогось такого базового так важко досягти і не переноситься.
Pedro77

12

getchar () - це стандартна функція, яка на багатьох платформах вимагає натискання клавіші ENTER для отримання вводу, оскільки платформа буферизує введення, доки не буде натиснуто цю клавішу. Багато компілятори / платформи підтримують нестандартний getch (), який не дбає про ENTER (обходить буферизацію платформи, розглядає ENTER як просто інший ключ).


1
+1 Правильно, getchar()починає читати символи по черзі лише після натискання клавіші Enter
Andomar

32
getchar()не хвилює ENTER, він просто обробляє все, що надходить через stdin. Буферизація рядків, як правило, визначається поведінкою ОС / терміналу.
goldPseudo

1
@goldPseudo: Правда, але майже всі платформи вводять буфер, тому саме таку поведінку ви побачите в більшості практичних випадків. getch (), де підтримується, буде керувати або ігнорувати буферизацію. Я знаю, що деякі старі реалізації DOS обійшли буферизацію ОС для безпосередньої взаємодії з апаратним забезпеченням. Інші реалізації можуть змити stdin, щоб отримати однакову поведінку.
Eric J.

10
Тим не менше, це не getchar()те, що "вимагає від вас натиснути клавішу Enter", а оточення.
Benji XVI

1
Дуже стисло і легко зрозуміти. Інші відповіді з найвищим рейтингом просто підказують переважні непотрібні технічні деталі.
mzoz

6

Введення / виведення - це функція операційної системи. У багатьох випадках операційна система не передаватиме набраний символ програмі, доки не буде натиснуто клавішу ENTER. Це дозволяє користувачеві модифікувати введення (наприклад, зворотний простір та повторне введення тексту) перед тим, як відправити його до програми. Для більшості цілей це добре працює, представляє послідовний інтерфейс для користувача та позбавляє програму від необхідності мати справу з цим. У деяких випадках бажано, щоб програма отримувала символи з клавіш під час їх натискання.

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

Крім того, стандартний вихід зазвичай буферизується для підвищення ефективності. Це робиться бібліотеками C, і тому існує рішення C, яке полягає вfflush(stdout); написанні кожного символу. Після цього, чи відображатимуться символи негайно, залежить від операційної системи, але всі ОС, з якими я знайомий, негайно відображатимуть вихідні дані, тому зазвичай це не проблема.


6

Мені подобається відповідь Лукаса, але я хотів би це трохи докласти. Існує вбудована функція в termios.hnamed, cfmakeraw()яку людина описує як:

Це в основному те саме, що пропонував Лукас, і більше, ви можете бачити точні прапори, які він встановлює на сторінках керівництва: termios (3) .

Випадок використання


Зауважте, що cfmakeraw()це не портативне, нестандартне розширення POSIXtermios.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

так, ви можете зробити це і у вікнах, ось код нижче, використовуючи бібліотеку conio.h


6
Питання позначено тегом c , а не c ++
Spikatrix

@CoolGuy Ну що? Чому б комусь допомогло, якщо хтось відкрив саме те саме питання, але змінив тег з c на c ++?
Андреас Гафербург,

@AndreasHaferburg Оскільки тут у нас не було б відповідей на C ++, особливо не погано написаних, підморгнути підморгувати .
Sapphire_Brick

1

У мене була така проблема / запитання, яка з’явилась у завданні, над яким я зараз працюю. Це також залежить від того, з якого входу ви захоплюєтесь. я використовую

щоб отримати вхідні дані під час роботи програми, тож це має бути потоком файлів, пов’язаним із командою.

На машині ubuntu я маю тестувати / націлювати, це вимагало не просто

або

Мені довелося додати прапор --file, а також шлях до команди, приблизно так:

Зараз все копатично.


2
Зверніть увагу, що це рішення залежить від ОС.
Оттавіо Кампана

0

Цей код працював для мене. Увага: це не є частиною стандартної бібліотеки, навіть якщо більшість компіляторів (я використовую GCC) її підтримує.

Цей код визначає перше натискання клавіші.


0

" Як уникнути натискання за Enterдопомогою getchar()? "

Перш за все, вхід терміналу зазвичай є лінійним або повністю буферизованим. Це означає, що операційна система зберігає фактичний вхід з терміналу в буфер. Зазвичай цей буфер зливається з програмою, коли fe \nсигналізується / надається stdin. Це робить преса до Enter.

getchar()знаходиться лише на кінці ланцюжка. Він не має можливості фактично впливати на процес буферизації.


" Як я можу це зробити? "

Кювет getchar() в першу чергу, якщо ви не хочете використовувати певні системні виклики , щоб змінити поведінку терміналу явно , як добре пояснено в інших відповідях.

На жаль, немає стандартної функції бібліотеки, а з цим немає портативного способу очищення буфера при введенні одного символу. Однак існують рішення, що базуються на реалізації та не переносяться.


У Windows / MS-DOS, Є getch()і getche()функції вconio.h файлі заголовка , які виконують саме те, що ви хочете - читає один символ, не потрібно чекати, поки новий рядок очистить буфер.

Основна відмінність між getch()та getche()полягає в тому, getch()що не відразу виводить фактичний введений символ у консоль, тоді як getche()робить. Додатковий "e"стенд означає ехо .

Приклад:


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

Зверніть увагу, що вам потрібно ініціалізувати так званий стандартний екран за допомогою initscr()і закрити той самий за допомогою endwin()процедур.

Приклад:


Примітка: Вам потрібно викликати компілятор з -lncursesопцією, щоб лінкер міг шукати та знаходити бібліотеку ncurses.


-1

За замовчуванням бібліотека C буферизує вихід, поки не побачить повернення. Щоб негайно роздрукувати результати, використовуйте fflush:


2
Однак вихід є частиною проблеми. Той, хто запитує, хоче, щоб клавіші програми читали під час натискання та негайно друкували на екрані. Це проблема як із входом, так і з виходом.
Девід Торнлі,

тобто fflush змиває лише вихід, але getchar все одно не повернеться після натискання клавіші (клавіші будуть буферизовані).
NickSoft
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.