我有一个聊天客户端,它以原始终端模式接收输入,但我不知道在这种模式下处理输入。我需要知道两件事:
- 如何逐个字符地读取输入并显示它?我是否必须有某种读取循环,一次读取一个字符并将其存储在缓冲区中?
- 如果我希望我的服务器处理新行条目的输入,我是否必须在每个字符进入我的缓冲区时扫描它并查找
\n
?
此外,一个示例的逐字符读取循环刷新\n
将非常棒。谢谢!
在 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);
}
我为此推荐GNU readline 库。它负责获取输入行的繁琐工作,并允许用户使用退格键、左右箭头等编辑行,并使用向上箭头调用旧命令,甚至使用 ^R 搜索旧命令,等。Readline 安装了典型的类 unix 发行版,如 linux,但如果你没有它,你可以在这里找到它
编辑:这是一个最小的 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);
}