我从来都不记得我是怎么做到的,因为它很少出现在我身上。但是在C或c++中,从标准输入中读取字符而不等待换行符(按enter)的最佳方法是什么?
理想情况下,它也不会将输入字符回显到屏幕上。我只是想在不影响控制台屏幕的情况下捕捉击键。
我从来都不记得我是怎么做到的,因为它很少出现在我身上。但是在C或c++中,从标准输入中读取字符而不等待换行符(按enter)的最佳方法是什么?
理想情况下,它也不会将输入字符回显到屏幕上。我只是想在不影响控制台屏幕的情况下捕捉击键。
当前回答
CONIO.H
你需要的函数是:
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
conio.h的Linux c++实现 http://sourceforge.net/projects/linux-conioh
其他回答
ssinfod对Linux的回答的变体,对我的口味来说稍微干净一些,为wcout和wchar_t实现,并在没有错误的情况下擦除无效字符。
#include <functional>
//For Linux kbhit(). For Windows, use conio.h.
#ifdef __unix__
#include <sys/ioctl.h> //For FIONREAD.
#include <termios.h>
//Call this at program start to setup for kbhit.
void initTerminalInput()
{
//Disable internal buffering.
std::wcout << std::unitbuf;
//Turn off line buffering.
struct termios term;
tcgetattr(0, &term);
term.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &term);
setbuf(stdin, NULL);
}
//Returns 0 if there's no input character to read.
int kbhit()
{
static int nbbytes;
ioctl(0, FIONREAD, &nbbytes);
return nbbytes;
}
#endif
//Waits for and retrieves a single validated character, calling a validation function on each character entered and
//erasing any that are invalid (when the validation function returns false).
static wchar_t getWChar(std::function<bool(wchar_t)> validationFunction)
{
static wchar_t inputWChar;
do
{
//Wait until there's an input character.
while (!kbhit())
{
}
inputWChar = getwchar();
//Validate the input character.
if (validationFunction(inputWChar))
{
//Valid.
break;
}
else
{
//Erase the invalid character.
std::wcout << L"\b \b";
}
} while (true);
return inputWChar;
}
在下面的例子中,我想让用户输入1、2或3。输入的任何其他字符都不会显示,它将等待,直到按下其中一个有效字符:
int main()
{
#ifdef __unix__
initTerminalInput();
#endif
getWChar([] (wchar_t inputWChar)
{
return (inputWChar >= L'1' && inputWChar <= L'3');
});
return 0;
}
我使用kbhit()来查看是否存在一个字符,然后使用getchar()来读取数据。 在windows上,您可以使用“conio.h”。在linux上,您必须实现自己的kbhit()。
参见下面的代码:
// 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;
}
在WINDOWS上我这样做:
#include <Windows.h>
int main()
{
system("PAUSE>NUL");
if (GetKeyState(0x41/*(the 'A' key, choosen e.g.)*/) & (0xff80/*That stands for "Default state / Key Down"*/)) {
//whatever I want to do after 'A' is pressed
}
}
这里可以找到一个键的列表,每个键都有自己的十六进制值: https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
C和c++对I/O有一个非常抽象的看法,没有标准的方法来做你想做的事情。有从标准输入流中获取字符的标准方法(如果有的话),这两种语言都没有定义其他任何方法。因此,任何答案都必须是特定于平台的,可能不仅取决于操作系统,还取决于软件框架。
这里有一些合理的猜测,但如果不知道目标环境是什么,就无法回答您的问题。
下面是一个不向系统提供外壳的版本(在macOS 10.14上编写和测试)
#include <unistd.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
char* getStr( char* buffer , int maxRead ) {
int numRead = 0;
char ch;
struct termios old = {0};
struct termios new = {0};
if( tcgetattr( 0 , &old ) < 0 ) perror( "tcgetattr() old settings" );
if( tcgetattr( 0 , &new ) < 0 ) perror( "tcgetaart() new settings" );
cfmakeraw( &new );
if( tcsetattr( 0 , TCSADRAIN , &new ) < 0 ) perror( "tcssetattr makeraw new" );
for( int i = 0 ; i < maxRead ; i++) {
ch = getchar();
switch( ch ) {
case EOF:
case '\n':
case '\r':
goto exit_getStr;
break;
default:
printf( "%1c" , ch );
buffer[ numRead++ ] = ch;
if( numRead >= maxRead ) {
goto exit_getStr;
}
break;
}
}
exit_getStr:
if( tcsetattr( 0 , TCSADRAIN , &old) < 0) perror ("tcsetattr reset to old" );
printf( "\n" );
return buffer;
}
int main( void )
{
const int maxChars = 20;
char stringBuffer[ maxChars+1 ];
memset( stringBuffer , 0 , maxChars+1 ); // initialize to 0
printf( "enter a string: ");
getStr( stringBuffer , maxChars );
printf( "you entered: [%s]\n" , stringBuffer );
}