W następnym kodzie:
#include <stdio.h>
int main(void) {
int c;
while ((c=getchar())!= EOF)
putchar(c);
return 0;
}
Muszę nacisnąć, Enteraby wydrukować wszystkie wprowadzone litery getchar
, ale nie chcę tego robić, chcę tylko nacisnąć literę i od razu zobaczyć, jak wprowadzona przeze mnie litera powtórzy się bez naciskania Enter. Na przykład, jeśli naciśnę literę „a”, chcę zobaczyć obok niej inne „a” i tak dalej:
aabbccddeeff.....
Ale kiedy naciskam 'a' nic się nie dzieje, mogę pisać inne litery, a kopia pojawia się tylko wtedy, gdy naciskam Enter:
abcdef
abcdef
Jak mogę to zrobić?
cc -o example example.c
Do kompilacji używam polecenia w systemie Ubuntu.
Odpowiedzi:
W systemie Linux możesz modyfikować zachowanie terminala za pomocą
stty
polecenia. Domyślnie terminal będzie buforował wszystkie informacje do momentu Enternaciśnięcia, zanim nawet wyśle je do programu C.Szybki, brudny i niezbyt przenośny przykład zmiany zachowania z poziomu samego programu:
#include<stdio.h> #include<stdlib.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; }
Zauważ, że nie jest to naprawdę optymalne, ponieważ zakłada po prostu, że
stty cooked
jest to zachowanie, które chcesz po zakończeniu programu, zamiast sprawdzać oryginalne ustawienia terminala. Ponadto, ponieważ całe przetwarzanie specjalne jest pomijane w trybie surowym, wiele sekwencji klawiszy (takich jak CTRL-C lub CTRL-D ) nie będzie działać tak, jak się spodziewasz, bez jawnego przetwarzania ich w programie.Możesz uzyskać
man stty
większą kontrolę nad zachowaniem terminala, w zależności od tego, co chcesz osiągnąć.źródło
system
to zły pomysł.Zależy to od systemu operacyjnego, jeśli pracujesz w środowisku podobnym do systemu UNIX, flaga ICANON jest domyślnie włączona, więc dane wejściowe są buforowane do następnego
'\n'
lubEOF
. Wyłączając tryb kanoniczny, natychmiast otrzymasz postacie. Jest to również możliwe na innych platformach, ale nie ma prostego rozwiązania wieloplatformowego.EDYCJA: Widzę, że określiłeś, że używasz Ubuntu. Właśnie opublikowałem wczoraj coś podobnego, ale pamiętaj, że spowoduje to wyłączenie wielu domyślnych zachowań twojego terminala.
#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; }
Zauważysz, że każda postać pojawia się dwukrotnie. Dzieje się tak, ponieważ dane wejściowe są natychmiast przekazywane z powrotem do terminala, a następnie program umieszcza je z powrotem za pomocą
putchar()
. Jeśli chcesz odłączyć wejście od wyjścia, musisz również wyłączyć flagę ECHO. Możesz to zrobić, po prostu zmieniając odpowiednią linię na:źródło
getchar () to standardowa funkcja, która na wielu platformach wymaga naciśnięcia klawisza ENTER, aby uzyskać dane wejściowe, ponieważ platforma buforuje dane wejściowe do momentu naciśnięcia tego klawisza. Wiele kompilatorów / platform obsługuje niestandardową metodę getch (), która nie dba o ENTER (omija buforowanie platformy, traktuje ENTER jak po prostu inny klucz).
źródło
getchar()
zaczyna czytać kolejne znaki dopiero po naciśnięciu klawisza Entergetchar()
nie dba o ENTER, po prostu przetwarza wszystko, co przychodzi przez stdin. Buforowanie linii jest zwykle zachowaniem zdefiniowanym przez system operacyjny / terminal.getchar()
„wymaga naciśnięcia klawisza enter”, ale środowisko.I / O to funkcja systemu operacyjnego. W wielu przypadkach system operacyjny nie przekazuje wpisanego znaku do programu, dopóki nie zostanie naciśnięty klawisz ENTER. Pozwala to użytkownikowi modyfikować dane wejściowe (takie jak cofanie i przepisywanie) przed wysłaniem ich do programu. W większości przypadków działa to dobrze, zapewnia użytkownikowi spójny interfejs i zwalnia program z konieczności radzenia sobie z tym. W niektórych przypadkach pożądane jest, aby program pobierał znaki z klawiszy podczas ich naciskania.
Sama biblioteka C zajmuje się plikami i nie przejmuje się tym, w jaki sposób dane trafiają do pliku wejściowego. Dlatego w samym języku nie ma sposobu, aby uzyskać klawisze, gdy są naciskane; zamiast tego jest to specyficzne dla platformy. Ponieważ nie określono systemu operacyjnego ani kompilatora, nie możemy tego dla Ciebie wyszukać.
Ponadto standardowe wyjście jest zwykle buforowane w celu zapewnienia wydajności. Robią to biblioteki C, więc istnieje rozwiązanie w C, które jest
fflush(stdout);
po każdym zapisanym znaku. Później to, czy znaki zostaną wyświetlone natychmiast, zależy od systemu operacyjnego, ale wszystkie systemy operacyjne, które znam, natychmiast wyświetlą dane wyjściowe, więc zwykle nie stanowi to problemu.źródło
Podoba mi się odpowiedź Lucasa, ale chciałbym ją nieco rozwinąć. W
termios.h
named jest wbudowana funkcja,cfmakeraw()
którą człowiek opisuje jako: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. [...]
Zasadniczo robi to to samo, co zasugerował Lucas, a co więcej, możesz zobaczyć dokładne flagi, które ustawia na stronach podręcznika: termios (3) .
Przypadek użycia
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;
źródło
cfmakeraw()
jest to nieprzenośne, niestandardowe rozszerzenie POSIXtermios.h
i niekoniecznie jest dostępne.Ponieważ pracujesz nad pochodną Uniksa (Ubuntu), oto jeden ze sposobów - niezalecany, ale zadziała (o ile możesz dokładnie wpisywać polecenia):
echo "stty -g $(stty -g)" > restore-sanity stty cbreak ./your_program
Użyj przerwania, aby zatrzymać program, gdy jesteś nim znudzony.
Możesz zaoszczędzić, jeśli „stty sane” przywróci twoje ustawienia wystarczająco dokładnie do twoich celów. Format „-g” nie jest przenośny między wersjami „stty” (więc to, co jest generowane w Solarisie 10, nie będzie działać w Linuksie lub odwrotnie), ale koncepcja działa wszędzie. Opcja „stty sane” nie jest powszechnie dostępna, AFAIK (ale jest w systemie Linux).
źródło
Możesz dołączyć bibliotekę „ncurses” i użyć
getch()
zamiastgetchar()
.źródło
tak, możesz to zrobić również w systemie Windows, oto kod poniżej, używając biblioteki 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; }
źródło
Pojawił się ten problem / pytanie w zadaniu, nad którym obecnie pracuję. Zależy to również od wejścia, z którego pobierasz. ja używam
aby uzyskać dane wejściowe podczas działania programu, więc musi to być strumień pliku skojarzony z poleceniem.
Na maszynie ubuntu, którą muszę przetestować / wycelować, wymagało to czegoś więcej niż tylko
system( "stty -raw" );
lub
system( "stty -icanon" );
Musiałem dodać flagę --file, a także ścieżkę do polecenia, na przykład:
system( "/bin/stty --file=/dev/tty -icanon" );
Teraz wszystko jest dobrze.
źródło
Ten kod zadziałał dla mnie. Uwaga: to nie jest częścią standardowej biblioteki, nawet jeśli większość kompilatorów (ja używam GCC) ją obsługuje.
#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; }
Ten kod wykrywa pierwsze naciśnięcie klawisza.
źródło
Po pierwsze, wejście terminala jest zwykle liniowe lub w pełni buforowane. Oznacza to, że system operacyjny przechowuje rzeczywiste dane wejściowe z terminala do bufora. Zwykle ten bufor jest opróżniany do programu, gdy fe
\n
został zasygnalizowany / dostarczony wstdin
. Jest to na przykład wykonane przez prasę Enter.getchar()
znajduje się na końcu łańcucha. Nie ma możliwości faktycznego wpływania na proces buforowania.Rów
getchar()
w pierwszej kolejności, czy Do not chcesz używać konkretnych wywołań systemowych, aby zmienić zachowanie terminala wyraźnie jak dobrze wyjaśnione w innych odpowiedzi.Niestety nie ma standardowej funkcji bibliotecznej, a tym samym nie ma przenośnego sposobu na opróżnienie bufora przy wprowadzaniu pojedynczego znaku. Istnieją jednak rozwiązania oparte na implementacji i nieprzenośne.
W systemie Windows / MS-DOS w pliku nagłówkowym znajdują się funkcje
getch()
i , które robią dokładnie to, co chcesz - odczytują pojedynczy znak bez potrzeby czekania, aż nowa linia opróżni bufor.getche()
conio.h
Główna różnica między
getch()
igetche()
polega na tym,getch()
że nie wyświetla natychmiast rzeczywistego znaku wejściowego w konsoli, podczas gdygetche()
tak. Dodatkowe"e"
oznacza echo .Przykład:
#include <stdio.h> #include <conio.h> int main (void) { int c; while ((c = getche()) != EOF) { if (c == '\n') { break; } printf("\n"); } return 0; }
W Linuksie, sposób uzyskania bezpośredniego przetwarzania znaków i wyjście jest wykorzystanie
cbreak()
iecho()
opcje i tymgetch()
irefresh()
procedury w bibliotece ncurses.Zauważ, że musisz zainicjować tak zwany ekran standardowy za pomocą
initscr()
i zamknąć to samo zendwin()
procedurami.Przykład:
#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; }
Uwaga: Musisz wywołać kompilator z
-lncurses
opcją, aby konsolidator mógł przeszukać i znaleźć bibliotekę ncurses.źródło
Domyślnie biblioteka C buforuje dane wyjściowe, dopóki nie zobaczy wyniku. Aby natychmiast wydrukować wyniki, użyj
fflush
:while((c=getchar())!= EOF) { putchar(c); fflush(stdout); }
źródło