如何正确读取 C 中的标准输入?

How to correctly read stdin in C?

我想用 C 写一个类似 sgtpep/pmenu 的应用程序。

然后我开始看ncurses库。我的第一次尝试是能够 select 菜单。例如当我做ls | ./a.out时,它应该能够显示所有文件,并突出显示第一个,当我按UPDOWN,它会相应地改变突出显示不同的项目。

完整代码为here

程序没有收到任何按键。 mvprintw(n+1, 0, "%d\n", ch); 在循环内总是打印 -1.

然后我删除不相关的代码,得到最小的例子。

#include <stdio.h>

char buf[100];

int main(int argc, char **argv) {
    int ch;

    while (fgets(buf, sizeof(buf), stdin)) puts(buf);

    ch = getch();
    printf("%d\n", ch);
    ch = getch();
    printf("%d\n", ch);
    ch = getch();
    printf("%d\n", ch);
    ch = getch();
    printf("%d\n", ch);

    return 0;
}

ch 总是 -1。我怀疑 stdin 不干净,所以我在 fgets 之后使用 fflush(stdin),但结果相同。

那么 stdin 的正确阅读方式是什么?


UPD1

#include <stdio.h>

char buf[100];

int main(int argc, char **argv) {
    int ch;

    while (fgets(buf, sizeof(buf), stdin)) puts(buf);

    fflush(stdin);

    ch = getchar();
    printf("%d\n", ch);
    ch = getchar();
    printf("%d\n", ch);
    ch = getchar();
    printf("%d\n", ch);
    ch = getchar();
    printf("%d\n", ch);

    return 0;
}

我修改了程序,所以和ncurses没有关系了,但是当运行ls | ./a.out时,ch一直显示-1


UPD2

使用 newterm 重定向 in 并且 out 有效。

FILE *fd = fopen("/dev/tty", "r+");
set_term(newterm(NULL, fd, fd)); // instead of initscr()
noecho();
cbreak();
keypad(stdscr, TRUE);

print_menu(cur, n);

while (true) {
    ch = getch();

    if (ch == KEY_UP || ch == 'k') --cur;
    else if (ch == KEY_DOWN || ch == 'j') ++cur;

    cur = (cur + n) % n;

    print_menu(cur, n);
}

endwin();

如果您要以这种方式从 stdin 切换到 curses,则必须打开终端设备,例如,/dev/tty 当您完成读取标准输入。打开终端后,您可以使用 newterm (which has parameters for the input/output streams unlike initscr).

初始化 curses

有关示例,请参阅 ncurses test-program, or dialog

curses 是标准输入的替代品。

C 模型是用户在控制台中编写一行,这为他提供了行编辑功能。他可以返回 space 并使用箭头键在直线上移动,直到他满意为止。然后他按下回车键,程序得到整行。然后程序会对其进行处理,通常会提示输入下一行。

curses 接管了键盘,因此它可以用作小键盘 - 你可以玩一个 space 入侵者小游戏 "z" 意思是 "left" 和 "x" 意思是 "right"。因此,你不应该同时尝试从 stdin 读取,你会得到字符,但 curses 系统会以奇怪的方式与它们交互。通过诅咒独家阅读。

在你的上一个程序中,你读取了直到 EOF 的输入。因此,后续输入是 EOF。