简体   繁体   English

在 C 程序中捕获一个字符但显示其他内容

[英]Capture one character but display something else in C program

Is it even possible to write a C program that waits for the user input (like cin in C++) and when a user strikes just one key (no enter), let's say, "a" character on the keyboard the terminal would display "b" (and still wait for the enter)?是否甚至可以编写一个等待用户输入的 C 程序(如 C++ 中的cin ),当用户只敲击一个键(不输入)时,假设键盘上的“a”字符终端将显示“b "(仍然等待输入)? If so - how can I achieve this?如果是这样 - 我怎样才能做到这一点?

It is possible, but it's not what "standard" C deals with.这是可能的,但这不是“标准” C 处理的。 You need additional library to achieve this.您需要额外的库来实现这一点。

You need to realize that standard C (or C++) functions are not meant to work with "keys", or "screen", or "terminal".您需要意识到标准 C(或 C++)函数不适用于“键”、“屏幕”或“终端”。 The only thing they can perform is so-called "standard input and output", which is limited to writing characters somewhere , and reading characters from somewhere .他们唯一能做的就是所谓的“标准输入输出”,仅限于在某处写入字符,从某处读取字符。 Source and target of characters are not strictly defined and depend on what's attached to the IO streams.字符的源和目标没有严格定义,并且取决于附加到 IO 流的内容。

When you think about keys, screens, and terminals, you think about more specific things.当您考虑按键、屏幕和终端时,您会考虑更具体的事情。 You are not interested just with sending and receiving characters between some unspecified places.您对在某些未指定的地方之间发送和接收字符不感兴趣。 You want them to come exactly from keys, and end up on the screen.您希望它们完全来自按键,并最终出现在屏幕上。 For this, you need a library which would know how to work with keys and screen.为此,您需要一个知道如何使用键和屏幕的库。 One of such libraries is, for example, ncurses , but there are probably other alternatives.例如,其中一个库是ncurses ,但可能还有其他选择。

There are some potential issues related to use of additional libraries: availability, portability, maintainability, etc. You need to account for these factors when you decide what exactly library to use, because final choice depends on your requirements: what platform are you working with?有一些与使用附加库相关的潜在问题:可用性、可移植性、可维护性等。当您决定使用什么库时,您需要考虑这些因素,因为最终选择取决于您的要求:您正在使用什么平台? Do you want your code to be portable?你希望你的代码是可移植的吗?

For Windows , use the conio.h library function getch() , same for Linux but curses.h instead as an example:对于Windows ,使用conio.h库 function getch() ,与Linux相同,但以curses.h为例:

#include <stdio.h>
#include <conio.h> /* or curses if you are on linux*/


int main(void)
{
  int c = 0;

  c = getch();

  if (c == 'a')
  {
    putchar('b');
    getchar(); /* waiting for a newline */
  }

  return 0;
}

Beware getch() is not a standard C function.注意getch()不是标准的 C function。

Edit: As Lundin pointed out in the comments, this is an MS DOS implementation not Windows as I falsely flagged it.编辑:正如 Lundin 在评论中指出的那样,这是一个 MS DOS 实现,而不是 Windows,因为我错误地标记了它。

More about curses here and getch() here更多关于 curses heregetch() here

Since you are on Linux and you don't need to store the real input, you can try with termios.h :由于您使用的是 Linux 并且您不需要存储真实输入,您可以尝试使用termios.h

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

#define KEY_ENTER   0x000a
#define KEY_ESCAPE  0x001b

static struct termios term, oterm;

static int getch(void);
static int kbhit(void);
static int kbesc(void);
static int kbget(void);

static int getch(void)
{
    int c = 0;

    tcgetattr(0, &oterm);
    memcpy(&term, &oterm, sizeof(term));
    term.c_lflag &= ~(ICANON | ECHO);
    term.c_cc[VMIN] = 1;
    term.c_cc[VTIME] = 0;
    tcsetattr(0, TCSANOW, &term);
    c = getchar();
    tcsetattr(0, TCSANOW, &oterm);
    return c;
}

static int kbhit(void)
{
    int c = 0;

    tcgetattr(0, &oterm);
    memcpy(&term, &oterm, sizeof(term));
    term.c_lflag &= ~(ICANON | ECHO);
    term.c_cc[VMIN] = 0;
    term.c_cc[VTIME] = 1;
    tcsetattr(0, TCSANOW, &term);
    c = getchar();
    tcsetattr(0, TCSANOW, &oterm);
    if (c != -1) ungetc(c, stdin);
    return ((c != -1) ? 1 : 0);
}

static int kbesc(void)
{
    int c;
    
    while (kbhit())
    {
        c = getch();
    }
    return c;
}

static int kbget(void)
{
    int c;

    c = getch();
    return (c == KEY_ESCAPE) ? kbesc() : c;
}

int main(void)
{
    int c;

    while (1)
    {
        c = kbget();
        if (c == KEY_ENTER)
        {
            break;
        }
        putchar('b');
    }
    printf("\n");
    return 0;
}

For more complex things consider ncurses对于更复杂的事情,请考虑ncurses

If you're trying to change the input of the user before he presses enter, you'll need to change some configurations of the terminal.如果您在用户按下回车键之前尝试更改用户的输入,则需要更改终端的一些配置。 The code bellow will read the user input char by char and change any a's to b's.下面的代码将逐个字符地读取用户输入的字符并将任何 a 更改为 b。 It stops after enter has been pressed.按下回车后停止。

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

int main(void) {
  struct termios oldterm, newterm;
  tcgetattr(STDIN_FILENO, &oldterm); // save current config
  // set new config
  newterm = oldterm;
  newterm.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON);
  tcsetattr(STDIN_FILENO, TCSAFLUSH, &newterm);

  char ch = 0;
  while (read(STDIN_FILENO, &ch, 1) && ch != '\n') {
    if (ch == 'a') // show a's as b's
      write(STDOUT_FILENO, "b", 1);
    else if (ch == 127) // allows deleting chars
      write(STDOUT_FILENO, "\b \b", 3);
    else // other chars
      write(STDOUT_FILENO, &ch, 1);
  }

  // return to old config
  tcsetattr(STDIN_FILENO, TCSANOW, &oldterm);

  return 0;
}

You can save the real input by storing the read chars, ch , in an array.您可以通过将读取的字符ch存储在数组中来保存实际输入。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM