fflush(stdin)和fflush(stdout)

来源:互联网 发布:培训学校课时软件 编辑:程序博客网 时间:2024/06/05 07:25
fflush(stdin)比较容易理解,即清理标准输入流,把多余的仍未被保存的数据丢掉。

比如,下面这个小程序:

void main()

{

        int a;

        char str[10];

        cin>>a;

        cout<<a<<endl;

        cin>>str;

        cout<<str<<endl;

}

目地很简单:从stdin获得一个整数存入a,接着立马打印出来;从stdin获得一个字符串存入str,也立马打印出来。但是下面这种可能需要特别考虑:在首行输入了两个整数,在cin>>a之后,stdin缓冲还有一个整数没被读取。接下来,不等输入字符串,就直接把上面多出来的数字存入到str中去并打印。

某种程度上这是操作不规范造成的,但是程序应该要有健壮性,程序员应该提前预防这种不规范的操作。可以在程序界面上提示“请输入1个整数”,甚至有时候不厌其烦的强调和警告也必要。当然,本例为求简单,并不在UI友好方面做文章。这时,可以在cin>>str语句前插入fflush(stdin),如此一来就可以清空标准输入缓冲里多余的数据。

fflush(stdout)跟fflush(stdin)类似,是对标准输出流的清理,但是它并不是把数据丢掉,而是及时地打印数据到屏幕上。为了更好的理解它,需要知道一个事实:标准输出是以『行』为单位进行的,也即碰到\n才打印数据到屏幕。这就可能造成延时,如下面这几行代码:

int a;

printf_s("input one number:");

fflush(stdout);\\#1

scanf_s("%d",&a);

如果没有#1那行代码,在某些平台上就可能迟迟看不到"input one number"被打印到屏幕上来,因为它没有回车。这时候,fflush(stdout)就起到及时输出的作用。

但是在Windows平台上,似乎并看不出差别来。也即MSFT已经将stdout的输出改成及时生效了。

fflush函数被广泛使用在多线程、网络编程的消息处理中。

fflush(stdout):清空输出缓冲区,

***********************************************************************************************************************************************************************


***********************************************************************************************************************************************************************

1.       为什么 fflush(stdin) 是错的




首先请看以下程序:


#include <stdio.h>


int main( void )


{


    int i;


    for (;;) {


        fputs("Please input an integer: ", stdout);


        scanf("%d", &i);


        printf("%d\n", i);


    }


    return 0;


}


这个程序首先会提示用户输入一个整数,然后等待用户输入,如果用户输入的是整数,程序会输出刚才输入的整数,并且再次提示用户输入一个整数,然后等待用户输入。但是一旦用户输入的不是整数(如小数或者字母),假设 scanf 函数最后一次得到的整数是 2 ,那么程序会不停地输出“Please input an integer: 2”。这是因为 scanf("%d", &i); 只能接受整数,如果用户输入了字母,则这个字母会遗留在“输入缓冲区”中。因为缓冲中有数据,故而 scanf 函数不会等待用户输入,直接就去缓冲中读取,可是缓冲中的却是字母,这个字母再次被遗留在缓冲中,如此反复,从而导致不停地输出“Please input an integer: 2”。


 


也许有人会说:“居然这样,那么在 scanf 函数后面加上‘fflush(stdin);’,把输入缓冲清空掉不就行了?”然而这是错的!C和C++的标准里从来没有定义过 fflush(stdin)。也许有人会说:“可是我用 fflush(stdin) 解决了这个问题,你怎么能说是错的呢?”的确,某些编译器(如VC6)支持用 fflush(stdin) 来清空输入缓冲,但是并非所有编译器都要支持这个功能(linux 下的 gcc 就不支持),因为标准中根本没有定义 fflush(stdin)。MSDN 文档里也清楚地写着fflush on input stream is an extension to the C standard(fflush 操作输入流是对 C 标准的扩充)。当然,如果你毫不在乎程序的移植性,用 fflush(stdin) 也没什么大问题。以下是 C99 对 fflush 函数的定义:


 


int fflush(FILE *stream);


 


如果 stream 指向输出流或者更新流(update stream),并且这个更新流
最近执行的操作不是输入,那么 fflush 函数将把这个流中任何待写数据传送至
宿主环境(host environment)写入文件。否则,它的行为是未定义的。


原文如下:




int fflush(FILE *stream);






If stream points to an output stream or an update stream in which
the most recent operation was not input, the fflush function causes
any unwritten data for that stream to be delivered to the host environment
to be written to the file; otherwise, the behavior is undefined.


 


其中,宿主环境可以理解为操作系统或内核等。


 


    由此可知,如果 stream 指向输入流(如 stdin),那么 fflush 函数的行为是不确定的。故而使用 fflush(stdin)  是不正确的,至少是移植性不好的。


 


 


2.       清空输入缓冲区的方法 
 


        虽然不可以用 fflush(stdin),但是我们可以自己写代码来清空输入缓冲区。只需要在 scanf 函数后面加上几句简单的代码就可以了。


        /* C 版本 */
        #include <stdio.h> 




        int main( void )
        {
            int i, c;
            for ( ; ; )
            {
                fputs("Please input an integer: ", stdout);
                scanf("%d", &i);


                if ( feof(stdin) || ferror(stdin) )
                { /* 如果用户输入文件结束标志(或文件已被读完), */
                  /* 或者发生读写错误,则退出循环               */
            
                    /* do something */
                    break;
                }
                /* 没有发生错误,清空输入流。                 */
                /* 通过 while 循环把输入流中的余留数据“吃”掉 */
                while ( (c = getchar()) != '\n' && c != EOF ) ;
                /* 使用 scanf("%*[^\n]");(详见下面备注①) 也可以清空输入流, */


                /* 不过会残留 \n 字符。                          */


               printf("%d\n", i);
            }


             return 0;
        }




        /* C++ 版本 */
        #include <iostream>
        #include <limits> // 为了使用numeric_limits
 


        using std::cout;
        using std::endl;
        using std::cin; 
        using std::numeric_limits;
        using std::streamsize;
 


        int main()
        {
            int value; 
            for ( ; ; )
            {
                cout << "Enter an integer: ";
                cin >> value;
                if ( cin.eof() || cin.bad() )
                { // 如果用户输入文件结束标志(或文件已被读完),
                  // 或者发生读写错误,则退出循环






                  // do something
                    break;
                }
                // 读到非法字符后,输入流将处于出错状态,
                // 为了继续获取输入,首先要调用 clear 函数
                // 来清除输入流的错误标记,然后才能调用
                // ignore 函数来清除输入流中的数据。
                cin.clear();
                // numeric_limits<streamsize>::max() 返回输入缓冲的大小。
                // ignore 函数在此将把输入流中的数据清空。
                // 这两个函数的具体用法请读者自行查询。
                cin.ignore( numeric_limits<streamsize>::max(), '\n' );


                cout << value << '\n';
            }






            return 0;
        }


参考资料:


ISO/IEC 9899:1999 (E) Programming languages— C 7.19.5.2 The fflush function


The C Programming Language 2nd Edition By Kernighan & Ritchie


ISO/IEC 14882(1998-9-01)Programming languages — C++


****************************************
备注①:


   星号(*)表示读指定类型的数据但不保存
  
   [ ]  扫描字符集合——正则表达示,字符簇:当在一组方括号里使用^时,它表示“非”或“排除”的意思。
   [^\n] 定义一个集合, 即除'\n'外的任意字符


   详注: ANSI C 标准向 scanf() 增加了一种新特性,称为扫描集(scanset)。 扫描集定义一个字符集合,可由 scanf() 读入其中允许的字符并赋给对应字符数组。 扫描集合由一对方括号中的一串字符定义,左方括号前必须缀以百分号。 例如,以下的扫描集使 scanf() 读入字符 A、B 和 C:
    %[ABC]
   使用扫描集时,scanf() 连续吃进集合中的字符并放入对应的字符数组,直到发现不在集合中的字符为止(即扫描集仅读匹配的字符,一旦发现不匹配,马上返回)。返回时,数组中放置以 null 结尾、由读入字符组成的字符串。
    char nums[20]="";
    scanf("%[0-9]",nums);
    用字符 ^ 可以说明补集。把 ^ 字符放为扫描集的第一字符时,构成其它字符组成的命令的补集合,指示 scanf() 只接受未说明的其它字符。
    其他的详见《正则表达式》。
 


    
 










一个例子
xiaou发表评论于2007-7-16 14:09:40  
#include <iostream>
#include <stdexcept>//标准库异常类 
#include <limits> // 为了使用numeric_limits
using namespace std;
int main()
{
    int a,b;
    while(1){
        cin>>a>>b;
        try{
            if(cin.good()) cout<<a+b<<endl;
            else throw runtime_error("输入有误,重新输入?y OR n :");
        }
        catch(runtime_error err){
            cout<<err.what();
            char ch;
            cin.clear();
            cin.ignore( numeric_limits<streamsize>::max(), '\n' );
            cin>>ch;
            if(cin.bad()||ch!='y') break;
        }
    }//whlie()
    system("pause");



    
 


 


另一个例子:C和C++杂合的清空缓存方法(荐)
小鼬(游客)发表评论于2008-5-7 12:20:26  
#include<iostream>
using namespace std;


template <class T>
class input{
      T t;
public:
      input(char *s,T min,T max){
          cout<<s<<endl;
          while(1){
              if(cin>>t)
                  if(t<=max && t>=min)
                      break;
              //C和C++杂合的:
              cin.clear();
      int c;
       while( (c=getchar())!='\n'&&c!=EOF );


          }
      }
};


int main()
{
    input<int> o("test",-1,10);
    system("pause");
}
 
 
 

0 0
原创粉丝点击