如何在C语言stdin中正确支持箭头键(↑↓←→)?

How to correctly support arrow keys(↑↓←→) in C language stdin?

提问人:czg 提问时间:11/1/2022 最后编辑:oguz ismailczg 更新时间:11/1/2022 访问量:491

问:

我打算在 CentOS 中以 C 语言开发一个命令行工具,其代码如下:

// client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

int main(int argc, char *argv[])
{
    char command[128];

    while (1)
    {
        memset(command, 0, 128);
        printf("cli > ");
        if (fgets(command, 128, stdin) != NULL)
        {
            if (strcmp(command, "exit\n") == 0)
                break;
            if (strcmp(command, "\n") == 0)
                continue;
            printf("do something ... %s", command);
        }
    }

    return 0;
}

该程序可以工作,但是当我按下箭头键时,它不会按照我期望的方式执行。

我已经完成了一个简单的SQL的输入,现在光标停留在分号之后。

[root@olap tests]# gcc client.c 
[root@olap tests]# ./a.out 
cli > selectt * from table_001;

但是我拼错了第一个关键字,应该是,而不是。 我现在正在按左箭头键(←)来尝试修复这个错误。 但是光标没有像我预期的那样正确移动,它变成了以下内容。selectselectt

[root@olap tests]# gcc client.c 
[root@olap tests]# ./a.out 
cli > selectt * from table_001;^[[D^[[D^[[D^[[D^[[D^[[D^[[D

我应该如何修改程序来解决这个问题?

我希望能从擅长C开发的人那里得到帮助。

谢谢大家

c centos 标准丁

评论

4赞 paddy 11/1/2022
如果您想在终端中进行特殊的输入/输出,请考虑使用 ncurses 库或类似库。
1赞 Cheatah 11/1/2022
还可以考虑 readlineeditlinelibeditreplxx 等。
1赞 hyde 11/1/2022
对于 Linux,这是一个很强的候选者:tiswww.cwru.edu/php/chet/readline/rltop.html(注意许可证是 GPL)
0赞 hyde 11/1/2022
自己做这件事是一项艰巨的任务,如果你真的想自己做,我建议你写一个单独的模块(.c 和 .h)来做行输入。
1赞 dimich 11/1/2022
箭头和其他控制键的键代码序列取决于运行程序的终端。如果你不想使用 ncurses 或 readline,你应该从环境变量中读取终端类型并直接处理 termcap 数据库。TERM

答:

1赞 runwuf 11/1/2022 #1

您可能想尝试使用 getchncurses

此示例代码演示了如何检测箭头键并出现 CLI 提示符:

#include <string.h>
#include <ncurses.h>

int main(int argc, char *argv[])
{
  int ch,i;
  char command[128];

  initscr();
  clear();
  noecho();
  cbreak();
  keypad(stdscr, true);

  while (1) {
      printw("cli> ");
      for (i=0;;i++) {
          ch=getch();
            
          if (ch==KEY_UP||ch==KEY_LEFT||ch==KEY_RIGHT||ch==KEY_DOWN) {
              /* printw("arrow keys pressed!");
              command[i]='\0';
              break; */
              i--;
          }
          else if (ch=='\n') {
              if (i>0)
                  printw("\n");
              command[i]='\0';
              break;
          }
          else {
              command[i]=ch;
              printw("%c",ch);
          }
      }
      if (strcmp(command, "exit") == 0)
          break;
      else
          printw("%s\n", command);
        
    }
    endwin();

    return 0;
}
1赞 Tenobaal 11/1/2022 #2

考虑使用该库,您可以在此处找到它。它支持箭头键、输入历史记录和任何长度的输入(如果已配置)。readline

char *input = readline("cli >");
if (input)
{
    if (strcmp(command, "exit\n") == 0)
    ...
    free(input);
}