Przechwytywanie znaków ze standardowego wejścia bez czekania na naciśnięcie enter

Nigdy nie pamiętam, jak to robię, ponieważ pojawia się tak rzadko dla mnie. Ale w C lub c++, jaki jest najlepszy sposób, aby odczytać znak ze standardowego wejścia bez czekania na znak nowej linii (naciśnij enter).

Również najlepiej, aby nie echo znaku wejściowego do ekranu. Chcę tylko przechwytywać naciśnięcia klawiszy z zewnątrz wpływając na ekran konsoli.

 138
Author: jww, 2009-01-07

15 answers

Nie jest to możliwe przenośnie w czystym C++, ponieważ zbyt wiele zależy od używanego terminala, który może być podłączony do standardowego wejścia (zwykle są one buforowane liniowo). Można jednak użyć do tego biblioteki:

  1. conio dostępne z kompilatorami windows. Użyj funkcji _getch(), aby nadać znak bez czekania na klawisz enter. Nie jestem częstym programistą windows, ale widziałem, jak moi koledzy z klasy po prostu dołączają conio.h i używają go. Zobacz conio.h w Wikipedii. It lists getch, który jest deklarowany jako przestarzały w Visual C++.
  2. curses dostępne dla Linuksa, kompatybilne implementacje curses są również dostępne dla windows. Posiada również funkcję getch. (spróbuj man getch, Aby wyświetlić jego stronę podręcznika). Zobacz przekleństwa w Wikipedii.

Zalecałbym używanie przekleństw, jeśli dążysz do kompatybilności między platformami. To powiedziawszy, jestem pewien, że istnieją funkcje, których można użyć do wyłączenia buforowania linii (uważam, że nazywa się to "tryb surowy", w przeciwieństwie do " gotowane mode " (zajrzyj do man stty)). Klątwy załatwią to za Ciebie w przenośny sposób, jeśli się nie mylę.

 87
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-24 11:02:56

W Linuksie (i innych systemach uniksopodobnych) można to zrobić w następujący sposób:

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

char getch() {
        char buf = 0;
        struct termios old = {0};
        if (tcgetattr(0, &old) < 0)
                perror("tcsetattr()");
        old.c_lflag &= ~ICANON;
        old.c_lflag &= ~ECHO;
        old.c_cc[VMIN] = 1;
        old.c_cc[VTIME] = 0;
        if (tcsetattr(0, TCSANOW, &old) < 0)
                perror("tcsetattr ICANON");
        if (read(0, &buf, 1) < 0)
                perror ("read()");
        old.c_lflag |= ICANON;
        old.c_lflag |= ECHO;
        if (tcsetattr(0, TCSADRAIN, &old) < 0)
                perror ("tcsetattr ~ICANON");
        return (buf);
}

Zasadniczo musisz wyłączyć tryb kanoniczny(i Tryb echo, aby stłumić Echo).

 72
Author: Falcon Momot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-22 14:07:40

CONIO.H

Potrzebne funkcje to:

int getch();
Prototype
    int _getch(void); 
Description
    _getch obtains a character  from stdin. Input is unbuffered, and this
    routine  will  return as  soon as  a character is  available  without 
    waiting for a carriage return. The character is not echoed to stdout.
    _getch bypasses the normal buffering done by getchar and getc. ungetc 
    cannot be used with _getch. 
Synonym
    Function: getch 


int kbhit();
Description
    Checks if a keyboard key has been pressed but not yet read. 
Return Value
    Returns a non-zero value if a key was pressed. Otherwise, returns 0.

Libconio http://sourceforge.net/projects/libconio

Lub

Linux C++ implementacja conio.h http://sourceforge.net/projects/linux-conioh

 15
Author: chrissidtmeier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-14 17:21:20

Znalazłem to na innym forum, szukając rozwiązania tego samego problemu. Zmodyfikowałem go trochę z tego, co znalazłem. Działa świetnie. Używam OS X, więc jeśli używasz Microsoft, musisz znaleźć odpowiednie polecenie system (), aby przełączyć się na tryby raw i gotowane.

#include <iostream> 
#include <stdio.h>  
using namespace std;  

int main() { 
  // Output prompt 
  cout << "Press any key to continue..." << endl; 

  // Set terminal to raw mode 
  system("stty raw"); 

  // Wait for single character 
  char input = getchar(); 

  // Echo input:
  cout << "--" << input << "--";

  // Reset terminal to normal "cooked" mode 
  system("stty cooked"); 

  // And we're out of here 
  return 0; 
}
 13
Author: cwhiii,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-26 19:26:39
#include <conio.h>

if (kbhit() != 0) {
    cout << getch() << endl;
}

To używa kbhit(), aby sprawdzić, czy klawiatura jest wciśnięta i używa getch(), aby uzyskać znak, który jest wciśnięty.

 8
Author: Joseph Dykstra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-23 20:18:50

Jeśli korzystasz z systemu windows, możesz użyć PeekConsoleInput , Aby wykryć, czy jest jakieś wejście,

HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
DWORD events;
INPUT_RECORD buffer;
PeekConsoleInput( handle, &buffer, 1, &events );

Następnie użyj ReadConsoleInput ,aby "pochłonąć" znak wejściowy ..

PeekConsoleInput(handle, &buffer, 1, &events);
if(events > 0)
{
    ReadConsoleInput(handle, &buffer, 1, &events);  
    return buffer.Event.KeyEvent.wVirtualKeyCode;
}
else return 0

Szczerze mówiąc, to jest z jakiegoś starego kodu, który mam, więc musisz trochę z nim pogrywać.

Najfajniejsze jest jednak to, że odczytuje dane wejściowe bez pytania o cokolwiek, więc znaki nie są wyświetlane w ogóle.

 7
Author: hasen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-08 08:56:55

Zakładając System Windows, spójrz na funkcję ReadConsoleInput .

 5
Author: Tritium,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-07 20:13:06

C i C++ przyjmują bardzo abstrakcyjny obraz We / Wy i nie ma standardowego sposobu na robienie tego, co chcesz. Istnieją standardowe sposoby pobierania znaków ze standardowego strumienia wejściowego, jeśli są jakieś do pobrania, a żaden inny język nie jest zdefiniowany. Każda odpowiedź będzie zatem musiała być specyficzna dla platformy, być może zależna nie tylko od systemu operacyjnego, ale także od struktury oprogramowania.

Jest tu kilka rozsądnych domysłów, ale nie ma sposobu, aby odpowiedzieć na twoje pytanie bez wiedząc, jakie jest twoje docelowe środowisko.

 5
Author: David Thornley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-07 20:59:51

Najbliższą rzeczą do przenośnego jest użycie ncurses biblioteka, aby umieścić terminal w "trybie cbreak". API jest gigantyczne; procedury, których najbardziej potrzebujesz, to

  • initscr i endwin
  • cbreak i nocbreak
  • getch
Powodzenia!
 5
Author: Norman Ramsey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-08 00:44:35

Poniżej znajduje się rozwiązanie zaczerpnięte z Expert C Programming: Deep Secrets , które ma działać na SVr4. Używa stty i ioctl .

#include <sys/filio.h>
int kbhit()
{
 int i;
 ioctl(0, FIONREAD, &i);
 return i; /* return a count of chars available to read */
}
main()
{
 int i = 0;
 intc='';
 system("stty raw -echo");
 printf("enter 'q' to quit \n");
 for (;c!='q';i++) {
    if (kbhit()) {
        c=getchar();
       printf("\n got %c, on iteration %d",c, i);
    }
}
 system("stty cooked echo");
}
 4
Author: PolyThinker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-08 09:04:38

Zawsze chciałem, aby pętla odczytała moje wejście bez naciskania klawisza return. to mi pomogło.

#include<stdio.h>
 main()
 {
   char ch;
    system("stty raw");//seting the terminal in raw mode
    while(1)
     {
     ch=getchar();
      if(ch=='~'){          //terminate or come out of raw mode on "~" pressed
      system("stty cooked");
     //while(1);//you may still run the code 
     exit(0); //or terminate
     }
       printf("you pressed %c\n ",ch);  //write rest code here
      }

    }
 2
Author: Setu Gupta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-19 19:10:25

Działa u mnie na windows:

#include <conio.h>
char c = _getch();
 2
Author: user1438233,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-12 06:40:51

Używam kbhit (), aby sprawdzić, czy jest obecny znak, a następnie getchar (), aby odczytać dane. W systemie windows możesz użyć "conio.h". W Linuksie będziesz musiał zaimplementować własną metodę kbhit().

Patrz kod poniżej:

// kbhit
#include <stdio.h>
#include <sys/ioctl.h> // For FIONREAD
#include <termios.h>
#include <stdbool.h>

int kbhit(void) {
    static bool initflag = false;
    static const int STDIN = 0;

    if (!initflag) {
        // Use termios to turn off line buffering
        struct termios term;
        tcgetattr(STDIN, &term);
        term.c_lflag &= ~ICANON;
        tcsetattr(STDIN, TCSANOW, &term);
        setbuf(stdin, NULL);
        initflag = true;
    }

    int nbbytes;
    ioctl(STDIN, FIONREAD, &nbbytes);  // 0 is STDIN
    return nbbytes;
}

// main
#include <unistd.h>

int main(int argc, char** argv) {
    char c;
    //setbuf(stdout, NULL); // Optional: No buffering.
    //setbuf(stdin, NULL);  // Optional: No buffering.
    printf("Press key");
    while (!kbhit()) {
        printf(".");
        fflush(stdout);
        sleep(1);
    }
    c = getchar();
    printf("\nChar received:%c\n", c);
    printf("Done.\n");

    return 0;
}
 2
Author: ssinfod,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-18 18:01:29

Możesz to zrobić przenośnie używając SDL( Simple DirectMedia Library), choć podejrzewam, że może Ci się nie spodobać jego zachowanie. Kiedy próbowałem, musiałem mieć SDL utworzyć nowe okno wideo (mimo że nie potrzebowałem go do mojego programu) i mieć to okno" chwycić " prawie wszystkie wejścia klawiatury i myszy (co było w porządku dla mojego użycia, ale może być denerwujące lub niewykonalne w innych sytuacjach). Podejrzewam, że to przesada i nie warto, chyba że całkowita przenośność jest koniecznością-w przeciwnym razie spróbuj jednego z innych sugerowane rozwiązania.

Nawiasem mówiąc, to da ci kluczowe wydarzenia prasowe i prasowe osobno, jeśli to lubisz.

 1
Author: Ruchira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-28 22:38:51

Ncurses zapewnia miły sposób, aby to zrobić! Jest to również mój pierwszy post (który pamiętam), więc wszelkie komentarze są mile widziane. Będę wdzięczny za przydatne, ale wszystkie są mile widziane!

Aby skompilować: g++ - std=c++11-pthread-lncurses .cpp-o

#include <iostream>
#include <ncurses.h>
#include <future>

char get_keyboard_input();

int main(int argc, char *argv[])
{
    initscr();
    raw();
    noecho();
    keypad(stdscr,true);

    auto f = std::async(std::launch::async, get_keyboard_input);
    while (f.wait_for(std::chrono::milliseconds(20)) != std::future_status::ready)
    {
        // do some work
    }

    endwin();
    std::cout << "returned: " << f.get() << std::endl;
    return 0;
}

char get_keyboard_input()
{
    char input = '0';
    while(input != 'q')
    {
        input = getch();
    }
    return input;
}
 1
Author: AngryDane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-26 22:02:43