清除緩衝區

清空緩衝區的方法
清空緩衝區的方法 1.輸入輸出緩衝區的概念(C++用的多一些)
    我想以一個例子說明,比如我想把一篇文章以字符序列的方式輸出到計算機顯示器屏幕上,那麼我的程序內存作爲數據源而顯示器驅動程序作爲數據目標,如果數據源直接對數據目標發送數據的話。數據目標獲得第一個字符,便將它顯示。然後從端口讀取下一個字符,可是這時就不能保證數據源向端口發送的恰好是第二個字符(也許是第三個,而第二個已經在數據目標顯示時發送過了)。這樣的話就不能保證輸出的數據能完整的被數據目標所接受並處理。
      爲了解決這個問題,我們需要在數據源與數據目標中間放置一個保存完整數據內容的區域,那就是 “緩衝區”。這樣的話,數據源可以不考慮數據目標正在處理哪部分數據,只要把數據輸出到緩衝區就可以了,數據目標也可以不考慮數據源的發送頻率,只是從緩衝區中依次取出下一個數據。從而保證了數據發送的完整性,同時也提高了程序的效率。
     當然getch(),getche()沒有用到緩衝區。
2.幾個函數的區別
    首先不要忘了,要用getch()必須引入頭文件conio.h,以前學C語言的時候,我們總喜歡用在程序的末尾加上它,利用它來實現程序運行完了暫停不退出的效果。如果不加這句話,在TC2.0的環境中我們用Ctrl+F9編譯並運行後,程序一運行完了就退回到TC環境中,我們根本來不及看到結果,這時要看結果,我們就要按Alt+F5回到DOS環境中去看結果,這很麻煩。而如果在程序的結尾加上一行getch();語句,我們就可以省掉會DOS看結果這個步驟,因爲程序運行完了並不退出,而是在程序最後把屏幕停住了,按任意鍵才退回到TC環境中去。
    那我們來看看getch()到底起的什麼作用,getch()實際是一個輸入命令,作用是從鍵盤接收一個字符,而且並不把這個字符顯示出來,就是說,你按了一個鍵後它並不在屏幕上顯示你按的什麼,而繼續運行後面的代碼,所以我們在C++中可以用它來實現“按任意鍵繼續”的效果,即程序中遇到getch ();這行語句,它就會把程序暫停下來,等你按任意鍵,它接收了這個字符鍵後再繼續執行後面的代碼。
    你也許會問,爲什麼我們在C++中就沒有在程序的末尾加上getch(),解釋是,軟件總是不斷更新的,不好的地方當然要進行改正,getch()加在程序末尾,它又不賦值給任何變量,所以它在這個地方完全是垃圾代碼,與程序無關。C++中考慮到這一點,於是在每次程序運行完了並不退出,而是自動把屏幕停下來,並顯示“press any key...”叫你按任意鍵退出,這就好比C++在它的環境中運行程序,在程序的末尾自動加上了一行getch();語句,並且在這行語句前還添加了一行輸出語句cout<<"press any key...";來提示你程序結束了,按任意鍵繼續。
    實際上我們編譯好的程序在程序結束了本身是不會停下來的,我們可以在編譯產生的Debug目錄中找到這個編譯好的應用程序(擴展名exe),在文件夾中雙擊運行它,你會發現屏幕閃了一下MS-DOS窗口就關閉了,因爲程序運行完就自動退出了,回到了windows環境,當然,如果我們在DOS環境中運行這個程序,我們就可以直接在看到DOS屏幕上看到程序運行結果,因爲程序運行完後並不清屏。但是,visual stdio.net2003有返回到了tc那樣的情況,你必需要有個getch()才行。
    getche()和getch()很相似,它也需要引入頭文件conio.h,那它們之間的區別又在哪裏呢?不同之處就在於getch()無返回顯示,getche()有返回顯示。就這麼一點看看下面的例子:

#include<stdio.h>
#include<conio.h>
void main()
{
    char ch;
    for(int i=0;i<5;i++)
    {
        ch=getch();
        printf("%c",ch);
    }
}

  首先這是個連續5次的循環來實現5次停頓,等待我們輸入,我們編譯並運行這個程序,假設我們分別輸入abcde,屏幕上顯示的結果是abcde,這個 abcde並不是在ch=getch();中輸出的,我們把printf("%c",ch);這行語句去掉,就會發現我們按5次任意鍵程序就結束了,但屏幕上什麼都沒有顯示。
    然後我們在把代碼中的getch()換成getche()看看有什麼不同,我們還是分別輸入abcde,這時屏幕上顯示的結果是aabbccddee,我們把printf("%c",ch);這行語句再去掉看看,顯示的結果就是abcde了,說明程序在執行ch=getche();這條語句的時候就把我們輸入的鍵返回顯示在屏幕上,有無回顯就是它們的唯一區別。
    有人會說,既然是C的函數庫中的,那麼就應該淘汰了,我們還研究它,還用它幹嘛?但是我發現還是有用着它的地方,否則我也不會在這裏說這麼多來耽誤大家的時間。我就舉個例子吧,程序如下:

#include<stdio.h>
#include<conio.h>
void main()
{
    char ch='*';
    while(ch=='*')
    {
        printf("/n按 * 繼續循環,按其他鍵退出!");
        ch=getch();
    }
    printf("/n退出程序!");
}

  我們可以在這個循環體中添加我們想要的功能,程序中按*繼續循環,其他任意鍵退出,而且利用getch()無回顯的特性,我們不管按什麼,都不會在屏幕上留下痕跡,使我們的界面達到美觀效果,如果還有更好的辦法實現這個功能。例子:

void main()
{
    char c, ch;
    c=getch(); /*從鍵盤上讀入一個字符不回顯送給字符變量c*/
    putchar(c); /*輸出該字符*/
    ch=getche(); /*從鍵盤上帶回顯的讀入一個字符送給字符變量ch*/
    putchar(ch);
    printf("/n/n");
}

值得注意的是前面兩個函數都是從鍵盤讀入數據!
還有getchar是很值得研究的:getchar()是stdio.h中的庫函數,它的作用是從stdin流中讀入一個字符,也就是說,如果stdin有數據的話不用輸入它就可以直接讀取了。而getch()和getche()是conio.h中的庫函數,它的作用是從鍵盤接收字符。getchar帶有顯示。
與前面兩個函數的區別在於: getchar()函數等待輸入直到按回車才結束(前提是緩衝區沒有數據),回車前的所有輸入字符都會逐個顯示在屏幕上。但只有第一個字符作爲函數的返回值。

#include<stdio.h>
#include<conio.h>
void main()
{
    char c;
    c=getchar(); /*從鍵盤讀入字符直到回車結束*/
           //getchar()在這裏它只返回你輸入字符串的第一個字符,並把返回值賦值給c

    putchar(c); /*顯示輸入的第一個字符*/
    printf("/n/n");
}

    例四:呵呵,這個程序你運行一下,相信你又會有疑問了。這個就是從緩衝區中讀取了例子。第一次getchar()時,確實需要人工的輸入,但是如果你輸了多個字符,以後的getchar()再執行時就會直接從緩衝區中讀取了。

#include<stdio.h>
#include<conio.h>
void main()
{
    char c;
    while ((c=getchar())!='/n') /*每個getchar()依次讀入一個字符*/
        printf("%c",c); /*按照原樣輸出*/
    printf("/n/n");
}

  程序運行時,首先停下來,等你輸入一串字符串,輸入完畢後,它把你輸入的整個字符串都輸出來了,咦,你不是說getchar()只返回第一個字符麼,這裏怎麼?
因爲我們輸入的字符串並不是取了第一個字符就把剩下的字符串丟掉了,它還在我們的內存中,就好比,開閘放水,我們把水放到閘裏去以後,開一次閘就放掉一點,開一次就放掉一點,直到放光了爲止,這裏開閘動作就相當於調用一次getchar()。我們輸入的字符串也是這麼一回事,首先我們輸入的字符串是放在內存的緩衝區中的,我們調用一次getchar()就把緩衝區中裏出口最近的一個字符輸出,也就是最前面的一個字符輸出,輸出後,就把它釋放掉了,但後面還有字符串,所以我們就用循環把最前面的一個字符一個個的在內存中釋放掉,直到不滿足循環條件退出爲止。
例子中循環條件裏的'/n'實際上就是你輸入字符串後的回車符,所以意思就是說,直到遇到回車符才結束循環,而getchar()函數就是等待輸入(或緩衝區中的數據)直到按回車才結束,所以實現了整個字符串的輸出。當然,我們也可以把循環條件改一下,比如while ((c=getchar())!='a'),什麼意思呢,意思就是遇到字符'a'就停止循環,當然意思是如果你輸入“12345a213123/n”那麼只會輸出到a,結果是12345a。
再次注意:用getchar()它是從“流”中間去讀取,所以第一個getchar()接受的是剛剛中斷的流隊列中即將出列的第一個字符(不限於回車符,上面舉過例子了),如果流隊列不爲空,執行getchar()就繼續放水,直到把回車符也放空爲止,空了之後再在執行getchar()就停下等待你的輸入了;我們用getch()爲什麼每次都是等待用戶的輸入呢?因爲getch()是從鍵盤接收,即時的接收,並不是從stdin流中去讀取數據。
補充:按鍵盤上的回車產生了2個字符:回車符('/r')和換行符('/n')。回車符'/r'(CR:carriage return:倒車)使光標回到這行的首部,換行符('/n')(new line)然後再換行。
所以當輸入字符'w',並按下回車鍵以後。首先得到回車符。那個getchar函數結束了。但是還存在一個換行符。所以如果用getchar()來做判斷的時候。最好再寫一次getchar()清除緩衝區的'/n'.
3.如何清空輸入緩衝區的內容?
如果我想讓getchar()每次都能夠等待用戶輸入的話就要清空緩衝區,下面就介紹方法(不同平臺)
C 標準規定 fflush()函數是用來刷新輸出(stdout)緩存的。對於輸入(stdin),它是沒有定義的。但是有些編譯器也定義了 fflush( stdin )的實現,比如微軟的VC。其它編譯器是否也定義了 fflush( stdin )的實現應當查找它的手冊。GCC編譯器沒有定義它的實現,所以不能使用 fflush( stdin )來刷新輸入緩存。
對於沒有定義 fflush( stdin )的編譯器,可以使用 fgets()函數來代替它(比用 getchar()、scanf()等函數通用性好)。可以這樣忽略輸入流中留下的回車等其它輸入,從而使下一次的輸入總保持一個“乾淨”的狀態。(這個是任何平臺下都可以的)
// ...
char sbuf[1024];
// ...
fgets( sbuf, 1024, stdin );
// ...
在windows 的vc下面就可以這樣了:
for(int i=0;i<10;++i)
{
    char ch=getchar();
    fflush(stdin); //每次都會有等待狀態了
}
4.總結主要看getch(),getche()的是否顯示,getchar()是讀取流,而且和前面兩個函數不是一個庫。掌握清空緩衝區的方法。
二。fflush(   FILE   *   pStream   )     清空一個流 
  pStream可以是stdin, stdout, stderr, stdprn, stdaux
  flushall()     清空所有流  
  需要包含   stdio.h

三。

int n;
int ret;
do {
    printf( "Input an integer: " );
    ret = scanf( "%d", &n );
    while ( getchar() != '/n' ); /* Clear the input buffer */
} while ( ret != 1 );
/* 執行這一段函數 */

當用戶如果輸入一個數字的時候,那麼這個時候n定義的是一個整型就將這個整型接收
ret = scanf( "%d", &n );的意思不等於ret=n;
而是當n接收到一個整型值時候ret=1;
while ( ret != 1 );跳出循環
如果當用戶輸入一個字符類型的數據,那麼這個時候 n已定義爲一個整型就無法接收了
所以n沒有接收到值此時候ret=0;
而getchar正是來接受字符的,當用戶輸入了回車('/n')後接收完畢
跳出while ( getchar() != '/n' ); 注意這裏的while 順環體爲空語句

這個時候while ( ret != 1 );(因爲ret=0,條件爲真繼續執行該循環)
所以說如果你輸入了一個非int類型
那麼接下來的又會
printf( "Input an integer: " );
ret = scanf( "%d", &n );
(直到輸入爲int類型止)

 
四。
雖然不可以用 fflush(stdin),但是我們可以自己寫代碼來清空輸入緩衝區。只需要在 scanf 函數後面加上幾句簡單的代碼就可以了。

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

int main( void )
{
    int i, c;
    for (;;) {
        fputs("Please input an integer: ", stdout);
        if ( scanf("%d", &i) != EOF ) { /* 如果用戶輸入的不是 EOF */
            /* while循環會把輸入緩衝中的殘留字符清空 */
            /* 可以根據需要把它改成宏或者內聯函數 */
            /* 注:C99中也定義了內聯函數,gcc3.2支持 */
            while ( (c=getchar()) != '/n' && c != EOF ) {
                  ;
            } /* end of while */
        }
        printf("%d/n", i);
    }
    return 0;
}

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

using std::cout;
using std::endl;
using std::cin;
int main( )
{
    int value;
    for (;;) {
    cout << "Enter an integer: ";
    cin >> value;
    /* 讀到非法字符後,輸入流將處於出錯狀態,
     * 爲了繼續獲取輸入,首先要調用clear函數
     * 來清除輸入流的錯誤標記,然後才能調用
     * ignore函數來清除輸入緩衝區中的數據。 */

    cin.clear( );
    /* numeric_limits<streamsize>::max( ) 返回緩衝區的大小。
     * ignore 函數在此將把輸入緩衝區中的數據清空。
     * 這兩個函數的具體用法請自行查詢。 */

    cin.ignore( std::numeric_limits<std::streamsize>::max( ), '/n' );
    cout << value << '/n';
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章