【问题标题】:raw terminal mode - how to take in input?原始终端模式 - 如何接受输入?
【发布时间】:2012-10-18 08:02:23
【问题描述】:

我有一个聊天客户端,它以原始终端模式接收输入,但我不知道如何在这种模式下处理输入。我需要知道两件事:

  • 如何逐个字符读取输入并显示?我是否必须有某种读取循环,一次读取一个字符并将其存储在缓冲区中?
  • 如果我希望我的服务器处理新行输入时的输入,我是否必须在每个字符进入我的缓冲区时对其进行扫描并查找\n

此外,在\n 上刷新的逐个字符读取循环示例非常值得一看。谢谢!

【问题讨论】:

  • 如果只在接收到'\n' 时刷新输出,那么使用原始模式没有多大意义,是吗?
  • 客户端需要能够在用户键入时打印来自聊天室中其他客户端的传入消息,因此它需要打印一些退格键以将新聊天置于用户键入的内容之上。

标签: c io terminal


【解决方案1】:

在 Windows 中有一个非常有用的函数 kbhit() 但不幸的是在 linux 中没有。为此可以有多种方法。我们将为 linux 创建自己的 kbhit() 方法。当在输入缓冲区中检测到某些东西时,kbhit() 将返回非零值,否则它将返回 0 并继续。简而言之,它是非阻塞的。只要结果为真,就调用 getch() 方法来获取按下的键。

#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>

void changemode(int);
int  kbhit(void);
int main(void)
{
  int ch;
  changemode(1);
  while ( !kbhit() );      // Waiting for some keyboard input.

  // something has been detected. now get that.
  ch = getchar();

  printf("\nGot %c\n", ch);

  changemode(0);
  return 0;
}

void changemode(int dir)
{
  static struct termios oldt, newt;

  if ( dir == 1 )
  {
    tcgetattr( STDIN_FILENO, &oldt);
    newt = oldt;
    newt.c_lflag &= ~( ICANON | ECHO );
    tcsetattr( STDIN_FILENO, TCSANOW, &newt);
  }
  else
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt);
}

int kbhit (void) 
{
  struct timeval tv;
  fd_set rdfs;

  tv.tv_sec = 0;
  tv.tv_usec = 0;

  FD_ZERO(&rdfs);
  FD_SET (STDIN_FILENO, &rdfs);

  select(STDIN_FILENO+1, &rdfs, NULL, NULL, &tv);
  return FD_ISSET(STDIN_FILENO, &rdfs);

}

【讨论】:

    【解决方案2】:

    我为此推荐GNU readline library。它负责获取输入行的繁琐工作,并允许用户使用退格键、左右箭头等编辑行,并使用向上箭头调用旧命令,甚至使用 ^R 搜索旧命令, Readline 安装了典型的类 unix 发行版,如 linux,但如果你没有它,你可以找到它here

    编辑:这是一个最小的 readline 示例:

    #include <stdio.h>
    #include <readline/readline.h>
    #include <readline/history.h>
    
    int main(int argc, char ** argv)
    {
        while(1)
        {
            char * line = readline("> ");
            if(!line) break;
            if(*line) add_history(line);
            /* Do something with the line here */
        }
    }
    

    使用 gcc -o test test.c -lreadline -lncurses 编译。

    如果你不能使用 readline,getline 是一个替代方案:

    #include <stdio.h>
    int main()
    {
        char * line = NULL;
        size_t len;
        while(getline(&line, &len, stdin) >= 0)
           printf("I got: %s", line);
    }
    

    如果连 getline 都不可接受,您可以使用 fgets。它不会动态分配合适大小的缓冲区,所以太长的行会被截断。但至少它是标准C:

    #include <stdio.h>
    int main()
    {
        char buf[1000];
        while(fgets(buf, sizeof(buf), stdin)
            printf("I got: %s, line);      
    }
    

    【讨论】:

    • 我实际上正在处理一项不允许使用库的任务。不过这个库看起来真的很整洁,我会在空闲时间检查一下。手动做起来难吗?
    • 如果你不能使用 readline,getline 是一个不太强大的替代方案。它可以在 中找到,但它不是标准的 C - 它是 POSIX 扩展,因此它不会在任何地方都可用。我将用一个例子来更新答案。
    • 谢谢! getline 只会得到这条线,对吗?因此,如果我处于原始模式,如何在输入字符时显示它们?
    • 哦,我错过了原始部分。我只使用 ncurses 之类的库直接访问了 TTY,所以我想我不能帮助你。谷歌搜索得到了我this example,但它并没有做你想要的字符回显。对此感到抱歉。
    • 这根本不是回答 OP 的问题。所有这些输入法都是缓冲的,不会一次给用户一个字符的原始键盘输入。
    猜你喜欢
    • 1970-01-01
    • 2022-11-16
    • 1970-01-01
    • 2019-02-17
    • 1970-01-01
    • 1970-01-01
    • 2021-04-03
    • 1970-01-01
    • 2021-02-20
    相关资源
    最近更新 更多