Jak uniknąć naciskania klawisza Enter za pomocą funkcji getchar () w celu odczytania tylko jednego znaku?

80

W następnym kodzie:

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:

Ale kiedy naciskam 'a' nic się nie dzieje, mogę pisać inne litery, a kopia pojawia się tylko wtedy, gdy naciskam Enter:

Jak mogę to zrobić?

cc -o example example.cDo kompilacji używam polecenia w systemie Ubuntu.

javier
źródło
5
jest to bardzo zależne od sposobu przetwarzania danych wejściowych przez system operacyjny i terminal. jeśli określisz swoje środowisko operacyjne, prawdopodobnie uzyskasz lepsze odpowiedzi.
goldPseudo
Odpowiedź podana na cplusplus.com/forum/articles/19975 działa w systemie Windows korzystającym z interfejsu WinAPI.

Odpowiedzi:

72

W systemie Linux możesz modyfikować zachowanie terminala za pomocą sttypolecenia. 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:

Zauważ, że nie jest to naprawdę optymalne, ponieważ zakłada po prostu, że stty cookedjest 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 sttywiększą kontrolę nad zachowaniem terminala, w zależności od tego, co chcesz osiągnąć.

goldPseudo
źródło
Używanie systemto zły pomysł.
Sapphire_Brick
Za dużo surowego… Szukałem rozwiązania, które pozwoliłoby mi nie czekać na ENTER. To rozwiązanie hamuje wszystkie funkcje zacisków wejściowych.
ABC
93

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'lub EOF. 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.

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:

Lucas
źródło
9
+1 Chociaż tbh, prawdopodobnie nie jest to poziom kodu, z którym oryginalny plakat jest wygodny;)
Andomar
2
Świetny! Właśnie tego szukałem!
Denilson Sá Maia
Dzięki za pomoc wraz ze szczegółowymi komentarzami rozwiązała mój problem.
kaminsknator
To niewiarygodne, że coś tak podstawowego jest tak trudne do osiągnięcia i nieprzenośne.
Pedro77
12

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).

Eric J.
źródło
1
+1 Prawidłowo, getchar()zaczyna czytać kolejne znaki dopiero po naciśnięciu klawisza Enter
Andomar
32
getchar()nie dba o ENTER, po prostu przetwarza wszystko, co przychodzi przez stdin. Buforowanie linii jest zwykle zachowaniem zdefiniowanym przez system operacyjny / terminal.
goldPseudo
1
@goldPseudo: True, ale prawie wszystkie platformy buforują dane wejściowe, więc jest to zachowanie, które zobaczysz w większości praktycznych przypadków. getch (), jeśli jest obsługiwana, zarządza lub ignoruje buforowanie. Wiem, że niektóre stare implementacje DOS omijały buforowanie systemu operacyjnego, aby współdziałać bezpośrednio ze sprzętem. Inne implementacje mogą opróżniać stdin, aby uzyskać to samo zachowanie.
Eric J.
10
Niemniej jednak to nie getchar()„wymaga naciśnięcia klawisza enter”, ale środowisko.
Benji XVI
1
Bardzo zwięzłe i łatwe do zrozumienia. Inne najwyżej oceniane odpowiedzi zawierają przytłaczające, niepotrzebne szczegóły techniczne.
mzoz
6

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.

David Thornley
źródło
6

Podoba mi się odpowiedź Lucasa, ale chciałbym ją nieco rozwinąć. W termios.hnamed jest wbudowana funkcja, cfmakeraw()którą człowiek opisuje jako:

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

wtedy bielizny
źródło
Zauważ, że cfmakeraw()jest to nieprzenośne, niestandardowe rozszerzenie POSIXtermios.h i niekoniecznie jest dostępne.
Andrew Henle
4

Ponieważ pracujesz nad pochodną Uniksa (Ubuntu), oto jeden ze sposobów - niezalecany, ale zadziała (o ile możesz dokładnie wpisywać polecenia):

Użyj przerwania, aby zatrzymać program, gdy jesteś nim znudzony.

  • Linia „echo” zapisuje bieżące ustawienia terminala jako skrypt powłoki, który je przywróci.
  • Linia `` stty '' wyłącza większość specjalnego przetwarzania (tzw Control-D nie ma na przykład żadnego efektu) i wysyła znaki do programu, gdy tylko są dostępne. Oznacza to, że nie możesz już edytować swojego wpisu.
  • Linia „sh” przywraca oryginalne ustawienia terminala.

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).

Jonathan Leffler
źródło
2

Możesz dołączyć bibliotekę „ncurses” i użyć getch()zamiast getchar().

AShelly
źródło
1

tak, możesz to zrobić również w systemie Windows, oto kod poniżej, używając biblioteki conio.h

djaa2807
źródło
6
Pytanie jest oznaczone tagiem c , a nie c ++
Spikatrix
@CoolGuy I co z tego? Dlaczego komuś miałoby pomóc, gdyby ktoś otworzył dokładnie to samo pytanie, ale zmienił tag z c na c ++?
Andreas Haferburg
@AndreasHaferburg Ponieważ nie mielibyśmy tu odpowiedzi C ++, a zwłaszcza nie źle napisane jedynek, Wink .
Sapphire_Brick
1

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

lub

Musiałem dodać flagę --file, a także ścieżkę do polecenia, na przykład:

Teraz wszystko jest dobrze.

Chad Chabot
źródło
2
należy pamiętać, że to rozwiązanie zależy od systemu operacyjnego.
Ottavio Campana
0

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.

Ten kod wykrywa pierwsze naciśnięcie klawisza.

Firefnix
źródło
0

Jak uniknąć naciskania Enterz getchar()?

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 \nzostał zasygnalizowany / dostarczony w stdin. 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.


Jak mogę to zrobić?

Rówgetchar() 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()i getche()polega na tym, getch()że nie wyświetla natychmiast rzeczywistego znaku wejściowego w konsoli, podczas gdy getche()tak. Dodatkowe "e"oznacza echo .

Przykład:


W Linuksie, sposób uzyskania bezpośredniego przetwarzania znaków i wyjście jest wykorzystanie cbreak()i echo()opcje i tym getch()i refresh()procedury w bibliotece ncurses.

Zauważ, że musisz zainicjować tak zwany ekran standardowy za pomocą initscr()i zamknąć to samo z endwin()procedurami.

Przykład:


Uwaga: Musisz wywołać kompilator z -lncursesopcją, aby konsolidator mógł przeszukać i znaleźć bibliotekę ncurses.

RobertS wspiera Monikę Cellio
źródło
-1

Domyślnie biblioteka C buforuje dane wyjściowe, dopóki nie zobaczy wyniku. Aby natychmiast wydrukować wyniki, użyj fflush:

Andomar
źródło
2
Jednak produkcja jest częścią problemu. Pytający chce, aby program czytał klawisze po ich naciśnięciu i natychmiast drukował na ekranie. To jest zarówno kwestia wejścia, jak i wyjścia.
David Thornley,
tj. fflush tylko opróżnia wyjście, ale getchar nadal nie zwraca po naciśnięciu klawisza (klawisze będą buforowane).
NickSoft