LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1149|回复: 13

stdin 缓冲问题

[复制链接]
发表于 2004-5-17 08:57:38 | 显示全部楼层 |阅读模式
如果利用fgetc或者read之类的函数从标准输入读一个字符,我发现程序运行时必须输入一个字符并按回车,才能获得输入的字符。
有没有办法当用户输入一个字符后,程序能马上获得该字符?

可以修改tty属性,取消stdin行缓冲?
发表于 2004-5-17 10:31:49 | 显示全部楼层
是的,把它改为立刻缓冲

不过好像不是 stdin 吧,是对 stdout 做设置。

setvbuf()
etc. 有好几个函数,自己man一下
 楼主| 发表于 2004-5-17 12:04:33 | 显示全部楼层
试了,还是不行,
下边的程序,必须输入y<Enter>才能看到 y pressed
#include <stdio.h>

int main()
{
        char ch;
        setvbuf(stdin, NULL, _IONBF, 0);
        setvbuf(stdout,NULL, _IONBF, 0);
        printf(">");
        if((ch=fgetc(stdin))=='y')
                printf("y pressed\n");

        return 0;
}
发表于 2004-5-17 13:51:09 | 显示全部楼层
使终端处于非规范模式,即可实现这样的实时响应
针对tcgetattr函数返回的termios结构,就可以改变终端特性。
给出一个例子。

  1. #include <termios.h>
  2. #include <sys/types.h>
  3. #define SMALLSZ 10

  4. main(){
  5.         struct termios tdes;
  6.         int ttyfd;
  7.         ssize_t nread;
  8.         char smallbuf[SMALLSZ+1];

  9.         tcgetattr(ttyfd,&tdes);

  10.         tdes.c_lflag &= ~ICANON; /*关闭canonical即规范模式*/
  11.         tdes.c_cc[VMIN]=0; /*MIN给出了在来自终端的read调用返回之前,终端驱动程序所必须接 受的最小字符数。若为0,则在收到第一个字符时read调用立即返回*/
  12.         tdes.c_cc[VTIME]=50; /*输入超时值,在read调用时立即启动*/
  13.         tcsetattr(0,TCSAFLUSH,&tdes);

  14.         nread=read(0,smallbuf,SMALLSZ);
  15.         if(smallbuf[0]=='y') printf(" -- Yeah! You typed yes.\n");
  16. }
复制代码

运行结果:
debian:/tmp# ./a.out
y -- Yeah! You type yes.
发表于 2004-5-17 14:23:03 | 显示全部楼层
最初由 romy 发表
试了,还是不行,
下边的程序,必须输入y<Enter>才能看到 y pressed
#include <stdio.h>

int main()
{
        char ch;
        setvbuf(stdin, NULL, _IONBF, 0);
        setvbuf(stdout,NULL, _IONBF, 0);
        printf(">");
        if((ch=fgetc(stdin))=='y')
                printf("y pressed\n");

        return 0;
}


  

printf(">");
fflush(stdout);

fflush(stdin);
fflush(stdout);

试试看
发表于 2004-5-17 15:16:32 | 显示全部楼层
用文件描述(英文叫file descriptor或fd)
0代表stdin
1代表stdout

  1. #include <stdio.h>
  2. {
  3.    char buf[50];  //缓冲区
  4.    int fd=0;      //stdin
  5.    read(fd,buf,1);//从stdin读取一个字节到buf

  6.    buf[1]='\n';   //换行
  7.    bug[2]='\0'    //字符串结束
  8.    printf("%s",buf);//看结果
  9. }
复制代码
 楼主| 发表于 2004-5-17 15:37:05 | 显示全部楼层
试验结果:
home_king 兄的方法果然可以。多谢多谢
luoyong兄提议的办法我以前也试过,不行的。
发表于 2004-5-17 19:39:06 | 显示全部楼层
是啊, home_king 的方法是治本的,是彻底的改变了终端的缓冲方式,如果程序退出前没有恢复原来的终端方式的话,那么不仅这个程序,以后的终端都会采用这种新定义的方式。这是我的理论想法,不知道理解的对不对
 楼主| 发表于 2004-5-17 22:06:48 | 显示全部楼层
是的,因为这个程序退出时没有恢复TERM的设置,该终端在以后的运行中仍然保持实时响应特性。
另外,也可以通过命令行来达到同样的效果。
stty -icanon (具体记不大清楚,man stty 即可)

以前偶用过curses.h里头的一些函数做过类似的事情,不过时间长了,也不常用,忘光了:))
发表于 2004-5-17 22:42:03 | 显示全部楼层
这个当然,你不妨在一个xterm里执行该程序试试看,程序结束后,这个xterm以后的read调用(如su命令)都会采用改变后的非规范模式,这是我们不想看到的。
所以有必要在适当的步骤恢复原状,方法是
tdes.c_lflag |= ~ICANON;
也就是将ICANON置1,恢复为规范模式。

ps:我认为大家会理解到这一点,所以没有明说。:p
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表