Jak zasymulować „Naciśnij dowolny klawisz, aby kontynuować?”

87

Próbuję napisać program w C ++, w którym przy wpisywaniu przez użytkownika dowolnego znaku z klawiatury powinien on przejść do następnej linii kodu.

Oto mój kod:

char c;

cin>>c;

cout<<"Something"<<endl;

ale to nie działa, ponieważ przechodzi do następnej linii tylko wtedy, gdy wprowadzam jakiś znak, a następnie wciskam ENTER.

LUB

Jeśli tego użyję

cin.get() or cin.get(c)

po naciśnięciu Enter przechodzi do następnego wiersza instrukcji.

Ale chciałem, żeby przesuwał się do następnej linii po naciśnięciu dowolnego klawisza na klawiaturze, jak to zrobić?

itsaboutcode
źródło
O ile wiem, problem polega na tym, że twoja powłoka czeka, aż naciśniesz ENTER lub EOF, a następnie pozwoli programowi zająć się tym, co jest w buforze, lub coś w tym rodzaju. Może ktoś z większą wiedzą mógłby udzielić prawdziwego wyjaśnienia. Ale myślę, że nie jest to tak łatwe, jak się początkowo wydaje.
Lucas
7
Zdecydowanie najłatwiejszym i jedynym przenośnym sposobem jest zmiana monitu z „Naciśnij dowolny klawisz, aby kontynuować” na „Naciśnij klawisz Enter, aby kontynuować”.
rob mayoff
@Lucas - używam Maca z xcode.
itsaboutcode
@Lucas: To nie jest powłoka, to sam program.
Keith Thompson
@KeithThompson: To było ponad dwa lata temu, ale myślę, że próbowałem zaznaczyć, że kolejka wejściowa nie jest obsługiwana przez proces użytkownika, ale wewnątrz jądra.
Lucas

Odpowiedzi:

64

W systemie Windows:

system("pause");

oraz na Macu i Linuksie:

system("read");

wyświetli komunikat „Naciśnij dowolny klawisz, aby kontynuować ...” i oczywiście poczekaj na naciśnięcie dowolnego klawisza. Mam nadzieję, że to miałeś na myśli

MrMartin
źródło
5
systemwywołuje program zewnętrzny. Nie musisz dzwonić do zewnętrznego programu, aby czekać na jakikolwiek klucz! To tak, jak dzwonienie do innej osoby, aby włączyła komputer, gdy siedzisz przed nim - to powolne rozwiązanie.
GingerPlusPlus,
8
To prawda, ale to tylko jedna linia! Czasami po prostu nie warto oszczędzać komputerowi kilku cykli ...
Elliot Cameron
14
Zapomnij wolno, wywołuje pierwszy program o nazwie „pauza”, który znalazł w PATH, i nadaje mu poziom uprawnień programu wywołującego. Nawet jeśli jesteś studentem tylko testującym swój własny kod, nadal stanowi to ogromne zagrożenie bezpieczeństwa.
Anne Quinn
6
@XDfaceme - pauza to rzeczywisty program, który system () prosi o uruchomienie systemu Windows. Jeśli jednak istnieje inny program o nazwie „pauza” i system Windows najpierw go znajdzie, zamiast tego uruchomi go. Przypuszczam, że mogłem trochę przesadzić, ale nadal łatwiej jest po prostu użyć cin.get () i nacisnąć przycisk powrotu, aby wstrzymać / wznowić program
Anne Quinn
2
Nie jestem fanem absolutów. Jednoznaczne stwierdzenie, że system („pauza”) stanowi zagrożenie dla bezpieczeństwa i że jest zły, nawet podczas testowania własnego kodu, jest tutaj nieproporcjonalne. cin.get () może być właściwym rozwiązaniem, ale nie rozwiązuje zadawanego pytania ... cin.get () odpowiada tylko po naciśnięciu "enter" lub "return". Chodziło konkretnie o odpowiedź na każde naciśnięcie klawisza. system („pauza”) robi dokładnie to. Po pierwsze, jedynym miejscem, w którym uznałem to za przydatne, jest klasa deweloperska podczas debugowania z programu Visual Studio, więc myślę, że system („pauza”) działa
Gregor
52

Jeśli korzystasz z systemu Windows, możesz użyć tego, kbhit()który jest częścią biblioteki wykonawczej firmy Microsoft. Jeśli używasz Linuksa, możesz zaimplementować w kbhitten sposób ( źródło ):

#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>

int kbhit(void)
{
  struct termios oldt, newt;
  int ch;
  int oldf;

  tcgetattr(STDIN_FILENO, &oldt);
  newt = oldt;
  newt.c_lflag &= ~(ICANON | ECHO);
  tcsetattr(STDIN_FILENO, TCSANOW, &newt);
  oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
  fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);

  ch = getchar();

  tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
  fcntl(STDIN_FILENO, F_SETFL, oldf);

  if(ch != EOF)
  {
    ungetc(ch, stdin);
    return 1;
  }

  return 0;
}

Aktualizacja: Powyższa funkcja działa na OS X (przynajmniej na OS X 10.5.8 - Leopard, więc spodziewałbym się, że będzie działać na nowszych wersjach OS X). Tę treść można zapisać jako kbhit.ci skompilować zarówno w systemie Linux, jak i OS X z

gcc -o kbhit kbhit.c

W biegu z

./kbhit

Monituje o naciśnięcie klawisza i kończy działanie po naciśnięciu klawisza (nie ogranicza się do klawiszy Enter lub drukowalnych).

@Johnsyweb - wyjaśnij, co masz na myśli przez „szczegółową odpowiedź kanoniczną” i „wszystkie wątpliwości”. Również ponownie „wieloplatformowy”: Dzięki tej implementacji kbhit()możesz mieć tę samą funkcjonalność w programie C ++ na Linux / Unix / OS X / Windows - o jakich innych platformach się odnosisz?

Dalsza aktualizacja dla @Johnsyweb: aplikacje C ++ nie działają w hermetycznie zamkniętym środowisku C ++. Głównym powodem sukcesu C ++ jest interoperacyjność z C. Wszystkie główne platformy są zaimplementowane z interfejsami C (nawet jeśli wewnętrzna implementacja używa C ++), więc twoje określenie „starszego” wydaje się nie na miejscu. Dodatkowo, skoro mówimy o pojedynczej funkcji, dlaczego potrzebujesz do tego C ++ („C z klasami”)? Jak już wspomniałem, możesz pisać w C ++ i mieć łatwy dostęp do tej funkcjonalności, a użytkownicy Twojej aplikacji raczej nie będą przejmować się tym , jak ją zaimplementowałeś.

Vinay Sajip
źródło
Szkoda, że ​​nie ma standardowego sposobu obsługi konsoli, zawsze trzeba używać magii systemu operacyjnego
Vargas
1
@Johnsyweb: Dziękuję wam , ale Twoje przykład pokazuje funkcje, które negatywnie na mnie o C ++. Twoja kbhit()funkcja deklaruje terminal_settingszmienną w linii, która wydaje się nic nie robić, a jednak robi wszystko. Niektórzy mogą pomyśleć, że to fajne, ale uważam, że jest to niejasne do tego stopnia, że ​​jest nieczytelne.
Vinay Sajip,
1
@VinaySajip: Moja implementacja używa RAII , który jest jednym z najważniejszych idiomów w C ++. Chociaż w tym przykładzie nie jest to absolutnie konieczne, stwierdziłem, że dobrym nawykiem jest nabycie się, gdy chcesz zapisać jakiś stan i przywrócić go.
Johnsyweb,
4
@Johnsyweb: Zdaję sobie sprawę z RAII i korzyści, jakie przynosi, sam jestem starą ręką C ++. Moja uwaga pozostaje: nie ma wyraźnego związku między deklaracją a tym, co robi za kulisami. Chociaż może używać błyszczących idiomów C ++ zamiast zwykłego starego C, moim zdaniem niewiele robi, aby oświetlić, a bardzo zaciemnić, co się dzieje. Nie jest to w żaden sposób skierowane do Ciebie osobiście - to tylko opinia o tym, jak można użyć C ++ do stworzenia niepotrzebnie trudnego do zrozumienia kodu. Zejdę teraz z mydelniczki - powiedział Nuff.
Vinay Sajip
2
Jest fajnie i wszystko, ale wciąż łapie wszystkie poprzednie śmieci na stdin :-)
Tiago
8

Nie ma całkowicie przenośnego rozwiązania.

Pytanie 19.1 z często zadawanych pytań dotyczących comp.lang.c zawiera szczegółowe omówienie rozwiązań dla systemu Windows, systemów uniksopodobnych, a nawet MS-DOS i VMS.

Szybkie i niepełne podsumowanie:

  • Możesz skorzystać z cursesbiblioteki; wywołanie, cbreak()po którym następuje getch()(nie mylić z getch()funkcją specyficzną dla systemu Windows ). Zauważ, że cursesgeneralnie przejmuje kontrolę nad terminalem, więc prawdopodobnie będzie to przesada.
  • Możesz użyć ioctl()do manipulowania ustawieniami terminala.
  • W systemach zgodnych z POSIX tcgetattr()i tcsetattr()może być lepszym rozwiązaniem.
  • W system()systemie Unix można użyć do wywołania sttypolecenia.
  • W systemie MS-DOS możesz użyć getch()lub getche().
  • W systemie VMS (obecnie nazywanym OpenVMS SMG$) procedury zarządzania ekranem ( ) mogą załatwić sprawę.

Wszystkie te rozwiązania C powinny działać równie dobrze w C ++; Nie znam żadnego rozwiązania specyficznego dla C ++.

Keith Thompson
źródło
Wszystkie te rozwiązania wieloplatformowe mogą przydać się, gdy ktoś pisze funkcję, która jest zaimplementowana w celu wykonania właściwej czynności zależnej od platformy z wieloma preprocesorami, aby określić, czym jest ta platforma.
CashCow
6

Aby osiągnąć tę funkcjonalność, możesz skorzystać z biblioteki ncurses, która została zaimplementowana zarówno na Windowsie, jak i na Linuksie (i MacOS o ile wiem).

Kirill V. Lyadvinsky
źródło
Cóż, jest to rodzaj zadania, w którym nie mogę używać żadnych zewnętrznych bibliotek itp., Więc po prostu muszę pozostać w "kontekście rozdziału" lox.
itsaboutcode
2
Wtedy jedyną opcją jest zaimplementowanie potrzebnych funkcji dla każdego systemu operacyjnego, który planujesz obsługiwać.
Kirill V. Lyadvinsky,
1
ncurses jest mniej więcej klientem VT200. Trochę przesada, jeśli chodzi o zwykłe czytanie z TTY.
greyfade
5

W systemie Windows ten krótki program osiąga cel: getchwstrzymuje konsolę do momentu naciśnięcia klawisza ( https://www.geeksforgeeks.org/difference-getchar-getch-getc-getche/ )

#include<iostream.h>
#include<conio.h>

using namespace std;

void  check()
{
    char chk; int j;
    cout<<"\n\nPress any key to continue...";
    chk=getch();
    j=chk;
    for(int i=1;i<=256;i++)
      if(i==j) break;
    clrscr();
}

void main()
{
    clrscr();
    check();
    cout<<"\n\nIt works!";
    getch();
}

Należy zauważyć, że getch nie jest częścią biblioteki standardowej.

BoldX
źródło
1
Ten bardzo prosty program. Bardzo łatwo to zrozumieć
BoldX
1
Proszę wyjaśnić, w jaki sposób getch()różni się od cin.getlub cin>>, być może niektóre łącza do dokumentacji
user2622016
1
conio działa tylko w systemie Windows
Andrew Wolfe
4

Przyjrzałem się temu, co próbujesz osiągnąć, ponieważ pamiętam, że chciałem zrobić to samo. Zainspirowany Vinayem napisałem coś, co działa dla mnie i trochę to rozumiem. Ale nie jestem ekspertem, więc proszę, bądź ostrożny.

Nie wiem, skąd Vinay wie, że używasz Mac OS X. Ale powinno to działać mniej więcej tak z większością systemów unixopodobnych. Naprawdę pomocne, ponieważ zasób jest opengroup.org

Pamiętaj, aby opróżnić bufor przed użyciem funkcji.

#include <stdio.h>
#include <termios.h>        //termios, TCSANOW, ECHO, ICANON
#include <unistd.h>     //STDIN_FILENO


void pressKey()
{
    //the struct termios stores all kinds of flags which can manipulate the I/O Interface
    //I have an old one to save the old settings and a new 
    static struct termios oldt, newt;
    printf("Press key to continue....\n");

    //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;

    //two of the c_lflag will be turned off
    //ECHO which is responsible for displaying the input of the user in the terminal
    //ICANON is the essential one! Normally this 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 | ECHO );      

    //Those new settings will be set to STDIN
    //TCSANOW tells tcsetattr to change attributes immediately. 
    tcsetattr( STDIN_FILENO, TCSANOW, &newt);

    //now the char wil be requested
    getchar();

    //the old settings will be written back to STDIN
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt);

}


int main(void)
{
  pressKey();
  printf("END\n");
  return 0;
}

O_NONBLOCK też wydaje się być ważną flagą, ale nic mnie to nie zmieniło.

Doceniam, jeśli osoby z głębszą wiedzą skomentują to i udzielą porady.

Lucas
źródło
O_NONBLOCK powinno być ustawione, ponieważ bez tego getchar () (które wywołuje read ()) blokuje oczekiwanie na wejście. Rozwiązanie kbhit () informuje, czy klawisz został trafiony, bez czekania; możesz go użyć w pętli oczekiwania lub w jakimkolwiek innym celu, więc jest to bardziej ogólne rozwiązanie. Rozwiązanie bez O_NONBLOCK będzie czekać na naciśnięcie klawisza - OK w przypadku tego pytania, ale mniej ogólnie przydatne.
Vinay Sajip
4

Możesz użyć specyficznej dla Microsoft funkcji _getch :

#include <iostream>
#include <conio.h>
// ...
// ...
// ...
cout << "Press any key to continue..." << endl;
_getch();
cout << "Something" << endl;
Salman A
źródło
3

Działa to na platformie Windows: wykorzystuje bezpośrednio rejestry mikroprocesora i może być używane do sprawdzania naciśnięcia klawisza lub przycisku myszy

    #include<stdio.h>
    #include<conio.h>
    #include<dos.h>
    void main()
    {
     clrscr();
     union REGS in,out;
     in.h.ah=0x00;
     printf("Press any key : ");

     int86(0x16,&in,&out);
     printf("Ascii : %d\n",out.h.al);
     char ch = out.h.al;
     printf("Charcter Pressed : %c",&ch);
     printf("Scan code : %d",out.h.ah);
     getch();
    }
Rohit Vipin Mathews
źródło
3

Jeśli używasz programu Visual Studio 2012 lub starszego, użyj getch()funkcji, jeśli używasz programu Visual Studio 2013 lub nowszego, użyj _getch(). Będziesz musiał użyć #include <conio.h>. Przykład:

#include <iostream>
#include <conio.h>

int main()
{
   std::cout << "Press any key to continue. . .\n";
   _getch(); //Or getch()
}
nieznany z nazwiska
źródło
1

Możesz użyć procedury getchar .

Z powyższego linku:

/* getchar example : typewriter */
#include <stdio.h>

int main ()
{
  char c;
  puts ("Enter text. Include a dot ('.') in a sentence to exit:");
  do {
    c=getchar();
    putchar (c);
  } while (c != '.');
  return 0;
}
Nick Dandoulakis
źródło
3
Nie sądzę, żeby o to prosił OP, jeśli dobrze go rozumiem. Nadal musisz nacisnąć ENTER, aby wypełnić bufor, zanim getchar () będzie mógł go odczytać.
Lucas
0

Możesz także użyć getch () z conio.h. Takie jak to:

...includes, defines etc
void main()
{
//operator
getch(); //now this function is waiting for any key press. When you have pressed its     just     //finish and next line of code will be called
}

Tak więc, ponieważ UNIX nie ma conio.h, możemy symulować getch () za pomocą tego kodu (ale ten kod został już napisany przez Vinary, moja porażka):

#include <stdio.h>
#include <termios.h>
#include <unistd.h>

int mygetch( ) {
  struct termios oldt,
             newt;
  int            ch;
  tcgetattr( STDIN_FILENO, &oldt );
  newt = oldt;
  newt.c_lflag &= ~( ICANON | ECHO );
  tcsetattr( STDIN_FILENO, TCSANOW, &newt );
  ch = getchar();
  tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
  return ch;
}
m9_psy
źródło
-1

Jeśli teraz wyszukasz funkcję kbhit () w witrynie MSDN, zobaczysz, że jest ona przestarzała. Zamiast tego użyj _kbhit ().

#include <conio.h>
int main()
{
    _kbhit();
    return 0;
}
BG4WCE
źródło
-1
#include <iostream>
using namespace std;

int main () {
    bool boolean;
    boolean = true;

    if (boolean == true) {

        cout << "press any key to continue";
        cin >> boolean;

    }
    return 0;
}
Jeffery
źródło
-4

Po prostu użyj system("pause");polecenia.

Wszystkie inne odpowiedzi komplikują sprawę.

Czad
źródło