当前位置:首页 > 操作系统 > Ios

使用termios.h在C程序中询问用户输入时,如何使箭头键和退格键正常工作?

所以我有以下代码,它基本上只读取字符用户输入并打印它们直到输入’q’.

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

int main(void) {
    char c; 
    static struct termios oldtio, newtio;
    tcgetattr(0, &oldtio);
    newtio = oldtio;
    newtio.c_lflag &= ~ICANON;
    newtio.c_lflag &= ~ECHO;
    tcsetattr(0, TCSANOW, &newtio);

    printf("Give text: ");
    fflush(stdout);
    while (1) {
        read(0, &c, 1);
        printf("%c", c);
        fflush(stdout);
        if (c == 'q') { break; }
    }
    printf("n"); 
    tcsetattr(0, TCSANOW, &oldtio);

    return 0;
}

在主要功能的开头,我关闭了规范模式,让用户在给出时可以看到他的输入.我也关闭回声,所以像“^ [[A”在按下向上箭头键时不会弹出这样的东西.这可行,但我也能够将光标移动到终端窗口的上行,这并不好.有没有办法解决这个问题,以便用户只能在当前行内移动?

另一个问题是退格.当我按下它时,程序打印一个奇怪的符号(我假设是0x7f),而不是删除光标当前位置的字符.我应该以某种方式处理程序中的退格键输出,但我不知道该怎么做,因为它是这个奇怪的十六进制数.有什么提示吗?

我一直在考虑使用的一个选项是使用规范模式,因此箭头键和退格功能会自动使用.但是,规范模式逐行工作,因此在用户点击“Enter”之前不会显示文本.到目前为止,我还没有找到任何方法让用户在打字时看到他的输入.这甚至可能吗?

请,没有ncurses或readline建议.我想用termios.h做到这一点.

解决方法:

你看过手册了吗? (应该是man termios或者看看某个地方online)

在那里我发现ECHOE标志据说有以下效果:

If ICANON is also set, the ERASE character erases the preceding input character, and WERASE erases the preceding word.

这应该解决您的退格问题?

我还建议,您可以查看手册页中的示例.例如.你可以做以下事情:

newtio.c_lflag &= ~(ECHO | ECHOE | ICANON);

…一次只能在一行中设置多个标志.我知道手册对于初学者来说很难阅读,但你会习惯他们,你使用它们越多,他们查找C / POSIX函数等就越有效(以防你不使用它们)无论如何).

箭头键问题:也许你可以试试cfmakeraw() – 函数;它的描述很有希望.我没有时间进一步调查箭头键.但是,也许您在手册页中找到了其他有用的东西.

BTW:termios看起来很有趣,我总是想知道某些命令行程序正在使用哪些函数;通过你的问题学到了什么,谢谢!

编辑

我本周末做了一些研究.按下退格键时打印的“奇怪”符号很容易隐藏.它是ASCII值0x7f.所以添加一个简单的

if (c == 0x7f) { continue; }

…只是忽略退格键.或者以删除最后一个字符的方式处理它(请参阅下面的代码示例).

这个简单的解决方法不适用于箭头键,因为它们不是ASCII字符:/但是,这两个主题帮助我处理了这个问题:topic 1topic 2.基本上按箭头键会导致几个字符的序列被发送到stdin(有关更多信息,请参阅第二个链接).

这是我的完整代码(我认为)按您希望的方式工作:

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

int main(void) {
    char c;
    static struct termios oldtio, newtio;
    tcgetattr(0, &oldtio);
    newtio = oldtio;
    newtio.c_lflag &= ~ICANON;
    newtio.c_lflag &= ~ECHO;
    tcsetattr(0, TCSANOW, &newtio);

    printf("Give text:n");
    fflush(stdout);
    while (1) {
        c = getchar();

        // is this an escape sequence?
        if (c == 27) {
            // "throw away" next two characters which specify escape sequence
            c = getchar();
            c = getchar();
            continue;
        }

        // if backspace
        if (c == 0x7f) {
            // go one char left
            printf("b");
            // overwrite the char with whitespace
            printf(" ");
            // go back to "now removed char position"
            printf("b");
            continue;
        }

        if (c == 'q') {
            break;
        }
        printf("%c", c);
    }
    printf("n");
    tcsetattr(0, TCSANOW, &oldtio);

    return 0;
}

顺便说一下,你可以通过以下代码获得完整的转义序列:

int main(void) {
    char c;
    while (1) {
        c = getchar();
        printf("%d", c);
    }
    return 0;
}

我想我不必说这个完整的东西是一个非常脏的黑客,很容易忘记处理一些特殊的密钥.例如.在我的代码中,我不处理页面向上/向下或主页键… – >上面的代码远非完整,但为您提供了一个重点.你还应该看看terminfo,它可以为你提供很多必要的信息;它还应该有助于提供更便携的解决方案.如你所见,这个“简单”的东西会变得相当复杂.所以你可能会重新考虑你对ncurses的决定:)


【说明】本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:254677821)!