Как избежать нажатия Enter с помощью getchar()
В следующем коде:
#include <stdio.h>
int main(void) {
int c;
while ((c=getchar())!= EOF)
putchar(c);
return 0;
}
Я должен нажать Enter, чтобы напечатать все буквы, которые я ввел getchar
, но я не хочу этого делать, то, что я хочу сделать, это нажать на букву и сразу же увидеть введенную мною букву, не нажимая Enter. Например, если я нажимаю букву "а", я хочу видеть рядом с ней другую букву "а" и т. Д.
aabbccddeeff.....
Но когда я нажимаю "а", ничего не происходит, я могу писать другие буквы, и копия появляется только тогда, когда я нажимаю Enter:
abcdef
abcdef
Как я могу это сделать?
Я использую команду cc -o example example.c
под Ubuntu для компиляции.
14 ответов
В системе Linux вы можете изменить поведение терминала, используя stty
команда. По умолчанию терминал будет буферизовать всю информацию до тех пор, пока не будет нажата Enter, прежде чем даже отправить ее в программу C.
Быстрый, грязный и не особо переносимый пример изменения поведения внутри самой программы:
#include<stdio.h>
int main(void){
int c;
/* use system call to make terminal send all keystrokes directly to stdin */
system ("/bin/stty raw");
while((c=getchar())!= '.') {
/* type a period to break out of the loop, since CTRL-D won't work raw */
putchar(c);
}
/* use system call to set terminal behaviour to more normal behaviour */
system ("/bin/stty cooked");
return 0;
}
Пожалуйста, обратите внимание, что это не совсем оптимально, так как предполагает, что stty cooked
это поведение, которое вы хотите при выходе из программы, а не проверять исходные настройки терминала. Кроме того, поскольку вся специальная обработка пропускается в необработанном режиме, многие последовательности клавиш (например, CTRL-C или CTRL-D) на самом деле не будут работать так, как вы ожидаете, без явной обработки их в программе.
Вы можете man stty
для большего контроля над поведением терминала, в зависимости от того, чего именно вы хотите достичь.
Это зависит от вашей ОС, если вы находитесь в UNIX-подобной среде, флаг ICANON включен по умолчанию, поэтому ввод буферизуется до следующего '\n'
или же EOF
, Отключив канонический режим вы сразу получите персонажей. Это также возможно на других платформах, но нет прямого кросс-платформенного решения.
РЕДАКТИРОВАТЬ: я вижу, вы указали, что вы используете Ubuntu. Я только что опубликовал нечто подобное вчера, но учтите, что это отключит многие стандартные настройки вашего терминала.
#include<stdio.h>
#include <termios.h> //termios, TCSANOW, ECHO, ICANON
#include <unistd.h> //STDIN_FILENO
int main(void){
int c;
static struct termios oldt, newt;
/*tcgetattr gets the parameters of the current terminal
STDIN_FILENO will tell tcgetattr that it should write the settings
of stdin to oldt*/
tcgetattr( STDIN_FILENO, &oldt);
/*now the settings will be copied*/
newt = oldt;
/*ICANON normally takes care that one line at a time will be processed
that means it will return if it sees a "\n" or an EOF or an EOL*/
newt.c_lflag &= ~(ICANON);
/*Those new settings will be set to STDIN
TCSANOW tells tcsetattr to change attributes immediately. */
tcsetattr( STDIN_FILENO, TCSANOW, &newt);
/*This is your part:
I choose 'e' to end input. Notice that EOF is also turned off
in the non-canonical mode*/
while((c=getchar())!= 'e')
putchar(c);
/*restore the old settings*/
tcsetattr( STDIN_FILENO, TCSANOW, &oldt);
return 0;
}
Вы заметите, что каждый персонаж появляется дважды. Это связано с тем, что ввод немедленно возвращается на терминал, а затем ваша программа возвращает его обратно putchar()
тоже. Если вы хотите отключить вход от выхода, вы также должны повернуть флаг ECHO. Вы можете сделать это, просто изменив соответствующую строку на:
newt.c_lflag &= ~(ICANON | ECHO);
getchar() - это стандартная функция, которая на многих платформах требует, чтобы вы нажали ENTER, чтобы получить ввод, потому что платформа буферизует ввод до тех пор, пока не будет нажата эта клавиша. Многие компиляторы / платформы поддерживают нестандартный метод getch(), который не заботится о ENTER (обходит буферизацию платформы, обрабатывает ENTER как просто другой ключ).
Ввод / вывод - это функция операционной системы. Во многих случаях операционная система не передает набранный символ в программу, пока не будет нажата клавиша ENTER. Это позволяет пользователю изменять ввод (такой как возврат и повторный ввод) перед отправкой его в программу. В большинстве случаев это работает хорошо, предоставляет пользователю согласованный интерфейс и избавляет программу от необходимости иметь дело с этим. В некоторых случаях желательно, чтобы программа получала символы от клавиш при их нажатии.
Сама библиотека C работает с файлами и не заботится о том, как данные попадают во входной файл. Поэтому в самом языке нет способа получить клавиши при их нажатии; вместо этого это зависит от платформы. Поскольку вы не указали ОС или компилятор, мы не можем найти его для вас.
Кроме того, стандартный вывод обычно буферизуется для эффективности. Это делается библиотеками C, и поэтому существует решение C, которое заключается в fflush(stdout);
после каждого написанного символа. После этого вопрос о том, отображаются ли символы немедленно, зависит от операционной системы, но все операционные системы, с которыми я знаком, немедленно отобразят вывод, так что обычно это не проблема.
Мне нравится ответ Лукаса, но я хотел бы немного его уточнить. Есть встроенная функция в termios.h
названный cfmakeraw()
который человек описывает как:
cfmakeraw() sets the terminal to something like the "raw" mode of the
old Version 7 terminal driver: input is available character by
character, echoing is disabled, and all special processing of
terminal input and output characters is disabled. [...]
Это в основном то же самое, что предложил Лукас, и более того, вы можете увидеть точные флаги, которые он устанавливает на страницах руководства: termios (3).
Случай использования
int c = 0;
static struct termios oldTermios, newTermios;
tcgetattr(STDIN_FILENO, &oldTermios);
newTermios = oldTermios;
cfmakeraw(&newTermios);
tcsetattr(STDIN_FILENO, TCSANOW, &newTermios);
c = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldTermios);
switch (c) {
case 113: // q
printf("\n\n");
exit(0);
break;
case 105: // i
printf("insert\n");
break;
default:
break;
Поскольку вы работаете с производной Unix (Ubuntu), вот один из способов сделать это - не рекомендуется, но он будет работать (при условии, что вы можете точно набирать команды):
echo "stty -g $(stty -g)" > restore-sanity
stty cbreak
./your_program
Используйте прерывание, чтобы остановить программу, когда вам скучно с ней.
sh restore-sanity
- Строка 'echo' сохраняет текущие настройки терминала в виде сценария оболочки, который восстановит их.
- Строка 'stty' отключает большую часть специальной обработки (например, Control-D не имеет никакого эффекта) и отправляет символы в программу, как только они становятся доступными. Это означает, что вы больше не можете редактировать свой набор текста.
- Линия 'sh' восстанавливает ваши первоначальные настройки терминала.
Вы можете сэкономить, если "stty sane" восстанавливает ваши настройки достаточно точно для ваших целей. Формат '-g' не переносится между версиями 'stty' (поэтому то, что генерируется в Solaris 10, не будет работать в Linux или наоборот), но концепция работает везде. Опция 'stty sane' не универсально доступна, AFAIK (но в Linux).
Вы можете включить библиотеку 'ncurses' и использовать getch()
вместо getchar()
,
"Как избежать нажатия Enter с
getchar()
?"
Прежде всего, вход терминала обычно либо линейный, либо полностью буферизованный. Это означает, что операционная система сохраняет фактический ввод с терминала в буфер. Обычно этот буфер сбрасывается в программу, когда fe\n
был сигнализирован / предоставлен в stdin
. Это делается с помощью пресса Enter.
getchar()
находится как раз в конце цепочки. У него нет возможности реально влиять на процесс буферизации.
"Как я могу это сделать?"
Канава getchar()
во-первых, если вы не хотите использовать определенные системные вызовы для явного изменения поведения терминала, как это хорошо объяснено в других ответах.
К сожалению, нет стандартной библиотечной функции, а также переносимого способа очистки буфера при вводе одного символа. Однако есть решения на основе реализации и непереносимые.
В Windows/MS-DOS есть getch()
а также getche()
функции в conio.h
Заголовочный файл, который делает именно то, что вы хотите - читаете один символ без необходимости ждать, пока новая строка очистит буфер.
Основное различие между getch()
а также getche()
в том, что getch()
не сразу выводит фактический входной символ в консоль, в то время как getche()
делает. Дополнительные"e"
означает эхо.
Пример:
#include <stdio.h>
#include <conio.h>
int main (void)
{
int c;
while ((c = getche()) != EOF)
{
if (c == '\n')
{
break;
}
printf("\n");
}
return 0;
}
В Linux для прямой обработки и вывода символов можно использовать cbreak()
а также echo()
варианты и getch()
а также refresh()
подпрограммы в библиотеке ncurses.
Обратите внимание, что вам нужно инициализировать так называемый стандартный экран с помощью initscr()
и закрыть то же самое с endwin()
рутины.
Пример:
#include <stdio.h>
#include <ncurses.h>
int main (void)
{
int c;
cbreak();
echo();
initscr();
while ((c = getch()) != ERR)
{
if (c == '\n')
{
break;
}
printf("\n");
refresh();
}
endwin();
return 0;
}
Примечание: вам нужно вызвать компилятор с -lncurses
вариант, чтобы компоновщик мог искать и находить библиотеку ncurses.
Да, вы можете сделать это и на Windows, вот код ниже, используя библиотеку conio.h
#include <iostream> //basic input/output
#include <conio.h> //provides non standard getch() function
using namespace std;
int main()
{
cout << "Password: ";
string pass;
while(true)
{
char ch = getch();
if(ch=='\r'){ //when a carriage return is found [enter] key
cout << endl << "Your password is: " << pass <<endl;
break;
}
pass+=ch;
cout << "*";
}
getch();
return 0;
}
Я получил эту проблему / вопрос в задании, над которым я сейчас работаю. Это также зависит от того, с какого входа вы получаете. я использую
/dev/tty
чтобы получить ввод во время работы программы, так что это должен быть файловый поток, связанный с командой.
На машине с Ubuntu я должен протестировать / нацелиться, это потребовало больше, чем просто
system( "stty -raw" );
или же
system( "stty -icanon" );
Мне пришлось добавить флаг --file, а также путь к команде, вот так:
system( "/bin/stty --file=/dev/tty -icanon" );
Теперь все в двух отношениях.
Этот код работал у меня. Внимание: это не часть стандартной библиотеки, даже если большинство компиляторов (я использую GCC) поддерживает ее.
#include <stdio.h>
#include <conio.h>
int main(int argc, char const *argv[]) {
char a = getch();
printf("You typed a char with an ASCII value of %d, printable as '%c'\n", a, a);
return 0;
}
Этот код обнаруживает первое нажатие клавиши.
Можно создать новую функцию, которая проверяет Enter:
#include <stdio.h>
char getChar()
{
printf("Please enter a char:\n");
char c = getchar();
if (c == '\n')
{
c = getchar();
}
return c;
}
int main(int argc, char *argv[])
{
char ch;
while ((ch = getChar()) != '.')
{
printf("Your char: %c\n", ch);
}
return 0;
}
По умолчанию библиотека C буферизирует выходные данные, пока не увидит возврат. Чтобы сразу распечатать результаты, используйте fflush
:
while((c=getchar())!= EOF)
{
putchar(c);
fflush(stdout);
}