【问题标题】:Capture one character but display something else in C program捕获一个字符但在 C 程序中显示其他内容
【发布时间】:2021-02-09 13:06:22
【问题描述】:

是否甚至可以编写一个等待用户输入的 C 程序(如 C++ 中的cin),当用户只敲击一个键(不输入)时,比如说,键盘上的“a”字符终端会显示“b”(仍然等待输入)?如果是这样 - 我怎样才能做到这一点?

【问题讨论】:

标签: c stdio


【解决方案1】:

这是可能的,但这不是“标准”C 处理的内容。您需要额外的库来实现这一点。

您需要意识到标准 C(或 C++)函数并不适用于“键”、“屏幕”或“终端”。他们唯一能做的就是所谓的“标准输入和输出”,它仅限于在somewhere中写入字符,以及从somewhere中读取字符。字符的源和目标没有严格定义,取决于附加到 IO 流的内容。

当您想到按键、屏幕和终端时,您会想到更具体的事物。您对在某些未指定的地方之间发送和接收字符不感兴趣。您希望它们完全来自按键,并最终出现在屏幕上。为此,您需要一个知道如何使用按键和屏幕的库。例如,ncurses 是此类库之一,但可能还有其他替代方案。

有一些与使用附加库相关的潜在问题:可用性、可移植性、可维护性等。当您决定使用什么库时,您需要考虑这些因素,因为最终选择取决于您的要求:什么平台你和你一起工作?您希望您的代码可移植吗?

【讨论】:

    【解决方案2】:

    对于 Windows,使用 conio.h 库函数 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;
    }
    

    注意getch() 不是标准的 C 函数。

    编辑:正如 Lundin 在 cmets 中指出的那样,这是一个 MS DOS 实现,而不是我错误标记的 Windows。

    更多关于诅咒heregetch()here

    【讨论】:

    • 这是一种 MS DOS 解决方案,而不是 Windows 解决方案。 Windows 将包括一个 WndProc 和一个窗口消息循环,或者控制台 API。
    【解决方案3】:

    由于您在 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;
    }
    

    对于更复杂的事情考虑ncurses

    【讨论】:

      【解决方案4】:

      如果您想在用户按下回车键之前更改用户的输入,则需要更改终端的一些配置。 下面的代码将逐个字符地读取用户输入的字符并将任何 a 更改为 b。按下回车后停止。

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

      您可以通过将读取的字符 ch 存储在一个数组中来保存实际输入。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-11-15
        • 2023-01-26
        • 2022-07-14
        • 2011-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多