tags:

views:

5593

answers:

10

I can never remember how I do this because it comes up so infrequently for me. But in C or C++, what is the best way to read a character from standard input without waiting for a newline (press enter).

Also ideally it wouldn't echo the input character to the screen. I just want to capture keystrokes with out effecting the console screen.

+16  A: 

That's not possible in C++, because it depends too much on the terminal used that may be connected with stdin (they are usually line buffered). You can, however use a library for that:

  1. conio available with windows compilers. Use the function _getch() to give you a character without waiting for the enter key. I'm not a frequent windows developer, but i've seen my classmates just include conio.h and use it. See conio.h at wikipedia. It lists getch, which is declared deprecated in Visual C++.
  2. curses available for linux, compatible curses implementations are available for windows too. It has also a getch function. (try man getch to view its manpage). See Curses at wikipedia.

I would recommend you to use curses if you aim for cross platform compatibility. That said, i'm sure there are functions that you can use to switch off line buffering (i believe that's called "raw mode", as opposed to "cooked mode" (look into man stty)). Curses would handle that for you in a portable manner if i'm not mistaken.

Johannes Schaub - litb
A: 

I think you need setvbuf().

setvbuf(stdin, NULL, _IONBF,0);

It should turn off buffering, but preventing echo is another problem... Also, you will still block until a character is available.

Roddy
+3  A: 

Assuming Windows, take a look at the ReadConsoleInput function.

Tritium
A: 

C and C++ take a very abstract view of I/O, and there is no standard way of doing what you want. There are standard ways to get characters from the standard input stream, if there are any to get, and nothing else is defined by either language. Any answer will therefore have to be platform-specific, perhaps depending not only on the operating system but also the software framework.

There's some reasonable guesses here, but there's no way to answer your question without knowing what your target environment is.

David Thornley
+2  A: 

Hi,

CONIO.H

the functions you need are:

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

or

Linux c++ implementation of conio.h http://sourceforge.net/projects/linux-conioh

A: 

The closest thing to portable is to use the ncurses library to put the terminal into "cbreak mode". The API is gigantic; the routines you'll want most are

  • initscr and endwin
  • cbreak and nocbreak
  • getch

Good luck!

Norman Ramsey
A: 

If you are on windows, you can use PeekConsoleInput to detect if there's any input,

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

then use ReadConsoleInput to "consume" the input character ..

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

to be honest this is from some old code I have, so you have to fiddle a bit with it.

The cool thing though is that it reads input without prompting for anything, so the characters are not displayed at all.

hasen j
A: 

The following is a solution extracted from Expert C Programming: Deep Secrets, which is supposed to work on SVr4. It uses stty and 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");
}
PolyThinker
+1  A: 

I found this on another forum while looking to solve the same problem. I've modified it a bit from what I found. It works great. I'm running OS X, so if you're running Microsoft, you'll need to find the correct system() command to switch to raw and cooked modes.

#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; 
}
cwhiii
While this works, for what it's worth, shelling out to the system is rarely the "best" way to do it in my opinion. The stty program is written in C, so you can include <termios.h> or <sgtty.h> and call the same code that stty uses, without depending on an external program/fork/whatnot.
Chris Lutz
+1  A: 

On Linux (and other unix-like systems) this can be done in following way:

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

char getch(void) {
        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);
}

Basically you have to turn off canonical mode (and echo mode to suppress echoing).