我从来都不记得我是怎么做到的,因为它很少出现在我身上。但是在C或c++中,从标准输入中读取字符而不等待换行符(按enter)的最佳方法是什么?
理想情况下,它也不会将输入字符回显到屏幕上。我只是想在不影响控制台屏幕的情况下捕捉击键。
我从来都不记得我是怎么做到的,因为它很少出现在我身上。但是在C或c++中,从标准输入中读取字符而不等待换行符(按enter)的最佳方法是什么?
理想情况下,它也不会将输入字符回显到屏幕上。我只是想在不影响控制台屏幕的情况下捕捉击键。
当前回答
假设是Windows,看一下ReadConsoleInput函数。
其他回答
管道演示:如何从C语言的系统调用管道读取键盘按键
理想情况下,它也不会将输入字符回显到屏幕上。我只是想在不影响控制台屏幕的情况下捕捉击键。
要在Linux上做到这一点,您可以使用以下bash命令:
read -sn1 c && printf "You Pressed: %s\n" "$c"
请看我在这里的回答关于这个的详细信息:shell脚本响应按键。
因此,要在Linux上的C或c++中做到这一点,您只需要通过使用popen()和fgets()的管道通过系统调用调用上面的bash命令,这样您就可以从bash命令读取输出。
下面是一个完整的例子,在Linux上运行良好的C和c++:
read_system_call_via_pipe__keypress.c:
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
#include <stdlib.h>
#define BUFSIZE 32
// Read a keyboard key press and return the character pressed, or a negative
// number in the event of an error.
// NB: for help reading output from system calls, see here:
// 1. https://stackoverflow.com/a/28971647/4561887
// 2. https://stackoverflow.com/a/18297075/4561887
char getKeypress()
{
// This bash cmd is from my answer here:
// https://stackoverflow.com/a/70979348/4561887
const char* cmd = "bash -c 'read -s -n1 c && printf \"%s\" \"$c\"'";
FILE *fp = popen(cmd, "r");
if (fp == NULL)
{
printf("\nError opening pipe!\n");
return -1;
}
char buf[BUFSIZE] = {0};
char* retval1 = fgets(buf, BUFSIZE, fp);
if (retval1 == NULL)
{
printf("\nFailed to read cmd response.\n");
return -2;
}
// See meaning of this return value here:
// https://stackoverflow.com/questions/43116/how-can-i-run-an-external-program-from-c-and-parse-its-output/28971647#comment60311936_28971647
int retval2 = pclose(fp);
if (retval2 == -1)
{
printf("\nError obtaining the cmd's exit status code.\n");
return -3;
}
else if (retval2 != 0)
{
printf("\nCommand exited with exit status code %i.\n", retval2);
return -4;
}
char keyPressed = buf[0];
return keyPressed;
}
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("Press any key to continue: ");
fflush(stdout);
char keyPressed = getKeypress();
if (keyPressed > 0)
{
printf("\nKey pressed = %c\n", keyPressed);
}
return 0;
}
C和c++编译和运行命令是下面输出的一部分。下面是一些演示:
在C:
eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=gnu17 read_keypress_system_call.c -o bin/a && bin/a
Press any key to continue:
Key pressed = P
或,在c++中:
eRCaGuy_hello_world/c$ g++ -Wall -Wextra -Werror -O3 -std=c++17 read_keypress_system_call.c -o bin/a && bin/a
Press any key to continue:
Key pressed = u
参见:
要更进一步,请参阅我在这里的另一个回答,我一次检测并解析3个字符,以便检测方向键的向上、向下、向左或向右:在C语言中读取键按下。方向键,Enter键
引用:
How I learned to read from a pipe to get system call output: How can I run an external program from C and parse its output? Is there a way to obtain the output of a linux command(like ifconfig) on a .txt file using a C program? [duplicate] How to compile and use popen() in C: use -std=gnu17 instead of -std=c17: popen implicitly declared even though #include <stdio.h> is added [my answer] How to read without blocking, via bash: shell script respond to keypress [my answer] How do I read in the Enter key as an input in C?
关于这个话题的3个问题
从标准输入中捕获字符,而不需要等待按enter键 C非阻塞键盘输入 如何避免按Enter与getchar()只为读取单个字符?
C和c++对I/O有一个非常抽象的看法,没有标准的方法来做你想做的事情。有从标准输入流中获取字符的标准方法(如果有的话),这两种语言都没有定义其他任何方法。因此,任何答案都必须是特定于平台的,可能不仅取决于操作系统,还取决于软件框架。
这里有一些合理的猜测,但如果不知道目标环境是什么,就无法回答您的问题。
以下是一个从Expert C Programming: Deep Secrets中提取的解决方案,它应该可以在SVr4上工作。它使用stty和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");
}
在Linux(和其他类unix系统)上,这可以通过以下方式完成:
#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);
}
基本上你必须关闭规范模式(和回声模式来抑制回声)。
我使用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;
}