,精彩!清空緩衝的方法)C/C++ 誤區二:fflush(stdin)

來源:互聯網
上載者:User
2008-5-7 12:02:00
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);’,把輸入緩衝清空掉不就行了?”然而這是錯的!CC++標準裡從來沒有定義過 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)  是不正確的,至少是移植性不好的。

 

 

  1. 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() 只接受未說明的其它字元。
    其他的詳見《Regex》。

   
一個例子
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");
}

 

C++的更好的方法:
2009-5-19 22:16:34
1、使用cin.clear()清除錯誤狀態
2、用cin.sync() 清空輸入緩衝區
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.