Domanda

Non riesco mai a ricordare come lo faccio perché mi capita così raramente.Ma in C o C++, qual è il modo migliore per leggere un carattere dallo standard input senza attendere un ritorno a capo (premere invio).

Inoltre, idealmente, non farebbe eco al carattere di input sullo schermo.Voglio solo catturare le sequenze di tasti senza influenzare lo schermo della console.

È stato utile?

Soluzione

Questo non è possibile in modo portatile in puro C ++, perché dipende troppo dal terminale utilizzato che può essere collegato con stdin (di solito sono buffer di linea). Puoi comunque usare una libreria per questo:

  1. conio disponibile con compilatori di Windows. Usa la funzione _getch() per darti un personaggio senza aspettare il tasto Invio. Non sono uno sviluppatore frequente di Windows, ma ho visto i miei compagni di classe includere solo <conio.h> e usarlo. Vedi conio.h su Wikipedia. Elenca getch(), che è dichiarato obsoleto in Visual C ++.

  2. maledizioni disponibili per Linux. Le implementazioni di maledizioni compatibili sono disponibili anche per Windows. Ha anche una funzione man getch. (prova man stty per visualizzare la sua manpage). Vedi Curses su Wikipedia.

Ti consiglierei di usare maledizioni se miri alla compatibilità multipiattaforma. Detto questo, sono sicuro che ci sono funzioni che puoi usare per disattivare il buffering di linea (credo che sia chiamato & Quot; modalità raw & Quot ;, al contrario di & Quot; modalità cucinata " - guarda in <=>). Le maledizioni lo gestiranno in modo portatile, se non sbaglio.

Altri suggerimenti

Su Linux (e altri sistemi simili a unix) questo può essere fatto nel modo seguente:

#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);
}

Fondamentalmente devi disattivare la modalità canonica (e la modalità eco per sopprimere l'eco).

L'ho trovato su un altro forum mentre cercavo di risolvere lo stesso problema. L'ho modificato un po 'da quello che ho trovato. Funziona benissimo. Sto usando OS X, quindi se stai usando Microsoft, dovrai trovare il comando system () corretto per passare alle modalità raw e cotte.

#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; 
}

conio.h

le funzioni di cui hai bisogno sono:

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

o

Implementazione Linux c ++ di conio.h http://sourceforge.net/projects/linux-conioh

#include <conio.h>

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

Usa kbhit() per verificare se la tastiera viene premuta e usa getch() per ottenere il carattere che viene premuto.

Se sei su Windows, puoi usare PeekConsoleInput per rilevare l'eventuale input,

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

quindi usa ReadConsoleInput su " consuma " il carattere di input ..

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

a dire il vero questo proviene da un vecchio codice che ho, quindi devi giocarci un po '.

La cosa interessante è che legge l'input senza richiedere nulla, quindi i caratteri non vengono visualizzati affatto.

Supponendo che Windows, dai un'occhiata alla funzione ReadConsoleInput .

C e C ++ hanno una visione molto astratta dell'I / O e non esiste un modo standard di fare ciò che vuoi. Esistono modi standard per ottenere caratteri dal flusso di input standard, se ce ne sono, e nient'altro è definito da entrambe le lingue. Pertanto, ogni risposta dovrà essere specifica per la piattaforma, forse a seconda non solo del sistema operativo ma anche del framework software.

Ci sono alcune ipotesi ragionevoli qui, ma non c'è modo di rispondere alla tua domanda senza sapere qual è il tuo ambiente di destinazione.

La cosa più vicina al portatile è usare la ncurses per mettere il terminale in " modalità cbreak " ;. L'API è gigantesca; le routine che vorrai di più sono

  • initscr e endwin
  • cbreak e nocbreak
  • getch

Buona fortuna!

Uso kbhit () per vedere se è presente un carattere e quindi getchar () per leggere i dati. Su Windows, puoi usare & Quot; conio.h & Quot ;. Su Linux, dovrai implementare il tuo kbhit ().

Vedi il codice qui sotto:

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

La seguente è una soluzione estratta da Programmazione Expert C: Deep Secrets , che dovrebbe funzionare su SVr4. Utilizza stty e 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");
}

Ho sempre voluto che un loop leggesse il mio input senza premere il tasto Invio. questo ha funzionato per me.

#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
      }

    }

funziona per me su Windows:

#include <conio.h>
char c = _getch();

Puoi farlo in modo portabile utilizzando SDL (la libreria Simple DirectMedia), anche se sospetto che potrebbe non piacerti il ​​suo comportamento.Quando l'ho provato, ho dovuto fare in modo che SDL creasse una nuova finestra video (anche se non ne avevo bisogno per il mio programma) e che questa finestra "catturasse" quasi tutti gli input da tastiera e mouse (che andava bene per il mio utilizzo ma poteva essere fastidioso o impraticabile in altre situazioni).Sospetto che sia eccessivo e non ne valga la pena a meno che la portabilità completa non sia un must, altrimenti prova una delle altre soluzioni suggerite.

A proposito, questo ti darà eventi di pressione e rilascio dei tasti separatamente, se ti piace.

ncurses fornisce un bel modo per farlo! Anche questo è il mio primo post (che ricordo), quindi tutti i commenti sono i benvenuti. Apprezzerò quelli utili, ma tutti sono i benvenuti!

da compilare: 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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top