首页 > 解决方案 > getchar() 避免转义序列

问题描述

我正在编写 Linux 控制台应用程序(某种菜单),所以我需要从用户那里获得输入。这是我到目前为止使用的代码:

int getch(void)
{
    int ch;
    struct termios oldt;
    struct termios newt;
    tcgetattr(STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_lflag &= static_cast<unsigned int>(~(ICANON | ECHO));
    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    ch = getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
    return ch;
}

#define KEY_UP 65
#define KEY_DOWN 66
#define KEY_ESC 27

while(true)
{
    ch = getch();

    switch(ch)
    {
        case KEY_UP:           
            break;
        case KEY_DOWN:
            break;
        case ESC:
            break;
    }
}

一切都很好,直到我需要使用 ESC。突然发现 getch() 有时会在循环中返回几个整数。例如:

KEY_UP:   27 91 65
KEY_DOWN: 27 91 66
BKSPACE:  127
ESC:      27

好的,在我的情况下,在循环中进行 3 次迭代后,我将获得正确的值(例如 65 表示 KEY_UP),但是当我真正单击 Esc 按钮时,如何获得 ESC 呢?如何配置 getch() 以仅返回实际值并避免 esc 序列?

Ubuntu 18.04 gcc 7.4.0 libstdc++.so.6

标签: c++console-applicationgetchar

解决方案


您可以获得真正的 ESC 键来实现一个功能,如果缓冲区中有更多待读取的数据,则kbhit基本上返回 true,这里是一个包装器,返回您的or (27) 的结果:kbhitkbgetgetchKEY_ESCAPE

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;

    if (!kbhit()) return KEY_ESCAPE;
    c = getch();
    if (c == '[') {
        switch (getch()) {
            case 'A':
                c = KEY_UP;
                break;
            case 'B':
                c = KEY_DOWN;
                break;
            case 'C':
                c = KEY_LEFT;
                break;
            case 'D':
                c = KEY_RIGHT;
                break;
            default:
                c = 0;
                break;
        }
    } else {
        c = 0;
    }
    if (c == 0) while (kbhit()) getch();
    return c;
}

static int kbget(void)
{
    int c;

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

推荐阅读