原始终端模式 - 如何接受输入?

时间:2012-10-28 23:57:07

标签: c io terminal

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

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

此外,在\n上刷新的逐字符读取循环示例真的很棒。谢谢!

2 个答案:

答案 0 :(得分:6)

我建议使用GNU readline library。它负责获取输入线的繁琐工作,并允许用户使用退格键,左箭头和右箭头等编辑其行,并使用向上箭头调用旧命令,甚至使用^ R搜索旧命令, Readline安装了类似Linux的典型unix类发行版,但是如果你没有它,你可以找到它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);      
}

答案 1 :(得分:5)

在Windows中有一个非常有用的函数kbhit()但不幸的是在linux中没有。可以有多种方法。我们将为linux创建自己的kbhit()方法。当在输入缓冲区中检测到某些内容时,kbhit()将返回非零值,否则它将返回0并传递。简单来说,它是非阻塞的。只要结果为true,则调用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);

}