首頁  >  文章  >  後端開發  >  使用C語言怎樣清空輸入緩衝區?這裡有多種方法值得借鏡

使用C語言怎樣清空輸入緩衝區?這裡有多種方法值得借鏡

php是最好的语言
php是最好的语言原創
2018-08-01 11:46:592981瀏覽

C語言中有幾個基本輸入函數:

<span style="color:#008000;">//获取字符系列</span><br>
int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);
//获取行系列
char *fgets(char * restrict s, int n, FILE * restrict stream);
char *gets(char *s);//可能导致溢出,用fgets代替之。
//格式化输入系列
int fscanf(FILE * restrict stream, const char * restrict format, …);
int scanf(const char * restrict format, …);
int sscanf(const char * restrict str, const char * restrict format, …);

這裡僅討論輸入函數在標準輸入(stdin)情況下的使用。綜觀上述各輸入函數,<br>

  • 取得字元系列的的前三個函數fgetc、getc、getchar。以getchar為例,將在stdin緩衝區為空時,等待輸入,直到回車換行時函數回傳。若stdin緩衝區不為空,getchar直接回傳。 getchar返回時從緩衝區中取出一個字符,並將其轉換為int,並傳回此int值。

MINGW 4.4.3中FILE結構體原始碼

  _iobuf
{
	char*	_ptr;//指向当前缓冲区读取位置
	int	_cnt;//缓冲区中剩余数据长度
	char*	_base;
	int	_flag;
	int	_file;
	int	_charbuf;
	int	_bufsiz;
	char*	_tmpfname;
} FILE;

各編譯器實作可能不一樣,這裡取得字元系列函數只用這裡取得字元系列函數只用這裡取得字元系列函數只用這裡取得字元到_ptr和_cnt。 <br>

MINGW 4.4.3中getchar()實作

__CRT_INLINE int __cdecl __MINGW_NOTHROW getchar (void)
{
  return (--stdin->_cnt >= 0)
    ?  (int) (unsigned char) *stdin->_ptr++
    : _filbuf (stdin);
}

其中stdin為FILE指標類型,在MINGW 4.4.3中,getc()和getchar()實作為內聯函數,fgetc()實作為函數。順便說一句,C99標準中已經加入了內聯函數的支援。

  • 取得行系列的fgets和gets,由於gets無法確定緩衝區大小,常導致溢出情況,這裡不推薦也不討論gets函數。對於fgets函數,每次敲入回車,fgets即回傳。 fgets成功返回時,將輸入緩衝區中的資料連換行符’\n’一起拷貝到第一個參數所指向的空間。若輸入資料超過緩衝區長度,fgets會截取資料到前n-1(n為fgets第二個參數,為第一個參數指向空間的長度),然後在末端加入’\n’。因此fgets是安全的。通常用fgets(buf, BUF_LEN, stdin);代替gets(buf);。在

  • 格式化輸入系列中,fscanf從檔案流進行格式化輸入很不好用。常用的還是scanf,格式化輸入系列函數捨去輸入資料(根據函數不同可能是標準輸入也可能是字串輸入,如:sscanf)前的空白字元(空格、製表符、換行符)直到遇到非空白字符,然後根據格式參數嘗試對非空白字符及後續字符進行解析。此系列函數傳回成功解析賦值的變數數,若遇文件尾或錯誤,返回EOF。

=================分割線=================

提到緩衝區,就不得不提setbufsetvbuf兩個緩衝區設定函數,其宣告如下:

 setbuf(FILE * restrict stream,  * restrict buf);
int setvbuf(FILE * restrict stream, char * restrict buf, int mode, size_t size);

setvbuf 的mode參數有:

  • _IOFBF(滿緩衝):緩衝區空時讀入資料;緩衝區滿時向流寫入資料。

  • _IOLBF(行緩衝):每次從流讀入一行資料或向流寫入資料。如:stdio,stdout

  • #_IONBF(無緩衝):直接從流讀入數據,或直接向流寫入數據,而沒有緩衝區。如:stderr

setbuf(stream, buf);在:

  • buf = = NULL:等價於(void)setvbuf(stream, NULL, _IONBF, 0);

  • buf指向長度為BUFSIZ的緩衝區:等價於(void)setvbuf(stream, buf, _IOFBF, BUFSIZ);

註:BUFSIZ巨集在stdio.h中定義。

這裡還要提一下傳說中的setbuf經典錯誤,在《C陷阱與缺陷》上有提到:

 main()
{
    int c;
    char buf[BUFSIZ];
<br>
    setbuf(stdout,buf);
    while((c = getchar()) != EOF)
        putchar(c);
    <br>
    return 0;
}

問題是這樣的:程式交回控制給作業系統之前C運行庫必須進行清理工作,其中一部分是刷新輸出緩衝,但是此時main函數已經運行完畢,buf緩衝區作用域在main函數中,此時buf字元陣列已經釋放,導致輸出詭異亂碼。

解決方案:可以將buf設定為static,或全域變量,或是呼叫malloc來動態申請記憶體。

=================分割線=================

#下面來看看幾種流行的緩衝區清空方法:

  • fflush(stdin);式

由C99標準文件中:

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.

可以看出fflush對輸入流為參數的行為並未定義。但由MSDN上的fflush定義:

If the file associated with stream is open for output, fflush writes to that file the
contents of the buffer associated with the stream. If the stream is open for input,
fflush clears the contents of the buffer.

可以看出fflush(stdin)在VC上還是有效地!鑑於各編譯器對fflush的未定義行為實作不一樣,不建議使用fflush(stdin)來刷新輸入緩衝區。

  • setbuf(stdin, NULL);式

由前面对setbuf函数的介绍,可以得知,setbuf(stdin, NULL);是使stdin输入流由默认缓冲区转为无缓冲区。都没有缓冲区了,当然缓冲区数据残留问题会解决。但这并不是我们想要的。

  • scanf("%*[^\n]");式(《C语言程序设计 现代方法 第二版》中提到)

这里用到了scanf格式化符中的“*”,即赋值屏蔽;“%[^集合]”,匹配不在集合中的任意字符序列。这也带来个问题,缓冲区中的换行符’\n’会留下来,需要额外操作来单独丢弃换行符。

  • 经典式

 c;
while((c = getchar()) != '\n' && c != EOF);

由代码知,不停地使用getchar()获取缓冲区中字符,直到获取的字符c是换行符’\n’或者是文件结尾符EOF为止。这个方法可以完美清除输入缓冲区,并且具备可移植性。<br>

相关文章:

禁止页面缓存的方法 多语言下禁止页面缓存

如何批量清理系统临时文件(语言:C#、 C/C++、 php 、python 、java )

相关视频:

C 语言教程

以上是使用C語言怎樣清空輸入緩衝區?這裡有多種方法值得借鏡的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn