Как избежать нажатия enter с getchar()


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

#include <stdio.h>

int main(void) {   
  int c;   
  while ((c=getchar())!= EOF)      
    putchar(c); 
  return 0;
}

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

aabbccddeeff.....

но когда я нажимаю "а" ничего не происходит, я могу писать другие письма и копия появляется только тогда, когда я нажимаю введите:

abcdef
abcdef

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

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

10 62

10 ответов:

в системе linux вы можете изменить поведение терминала с помощью

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

EDIT: я вижу, вы указали, что используете 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 как еще один ключ).

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

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

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

поскольку вы работаете над производной 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).

мне нравится ответ Лукаса, но я хотел бы немного уточнить его. Есть встроенная функция в 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. [...]

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

Use case

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;

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

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

while((c=getchar())!= EOF)      
{
    putchar(c);
    fflush(stdout);
}

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

#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

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

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

system( "stty -raw" );

или

system( "stty -icanon" );

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

system( "/bin/stty --file=/dev/tty -icanon" );

теперь все в порядке.