簡記C語言清空輸入殘留內容

来源:https://www.cnblogs.com/somebottle/archive/2022/07/14/Clear_STDIN.html
-Advertisement-
Play Games

為了在命令行程式中實現和用戶的交互,我們編寫的程式的運行過程中往往涉及到對標準輸入/輸出流的多次讀寫。 在C語言中接受用戶輸入這一塊,有著一個老生常談的問題:“怎麼樣及時清空輸入流中的數據?” 這也是這篇小筆記的主題內容。 先從緩衝區說起。 緩衝區是記憶體中劃分出來的一部分。通常來說,緩衝區類型有三種 ...


為了在命令行程式中實現和用戶的交互,我們編寫的程式的運行過程中往往涉及到對標準輸入/輸出流的多次讀寫。

在C語言中接受用戶輸入這一塊,有著一個老生常談的問題:“怎麼樣及時清空輸入流中的數據?”

這也是這篇小筆記的主題內容。

idling-2022-06-28

先從緩衝區說起。

緩衝區是記憶體中劃分出來的一部分。通常來說,緩衝區類型有三種:

  • 全緩衝
  • 行緩衝
  • 無緩衝

行緩衝

在C語言中緩衝區這個概念的存在感還是挺強的,比較常用到的緩衝區類型則是行緩衝了,如標準輸入流 stdin 和標準輸出流 stdout一般(終端環境下)就是在行緩衝模式下的。

行緩衝,顧名思義,就是針對該緩衝區的I/O操作基於行的。

  • 在遇到換行符前,程式的輸入輸出都會先被暫存流對應的緩衝區中

  • 而在遇到換行符後(或者緩衝區滿了),程式才會進行真正的I/O操作,將該緩衝區中的數據寫到對應的 (stream) 中以供後續讀取

標準輸入stdin而言,用戶的輸入首先會被存到相應的輸入緩衝區中,每當用戶按下回車鍵輸入一個換行符,程式才會進行I/O操作,將緩衝區暫存的數據寫入到stdin中,以供輸入函數使用。

stdinBuffer-2022-07-12

而對標準輸出stdout來說,輸出內容也首先會被暫存到相應的輸出緩衝區中,每當輸出數據遇到換行符時,程式才會將緩衝區中的數據寫入stdout,繼而列印到屏幕上。

這也是為什麼在緩衝模式下,輸出的內容不會立即列印到屏幕上:

#include <stdio.h>
int main()
{
	// 設置緩衝模式為行緩衝,緩衝區大小為10位元組
	setvbuf(stdout, NULL, _IOLBF, 10);
	fprintf(stdout, "1234567"); // 這裡先向stdout對應的緩衝區中寫入了7位元組
	getchar(); // 這裡等待用戶輸入
	printf("89"); // 再向stdout對應的緩衝區中寫入了2位元組
	getchar(); // 接著等待用戶輸入
	printf("Print!"); // 再向stdout對應的緩衝區中寫入了6位元組
	getchar(); // 最後再等待一次用戶輸入
	return 0;
}

運行效果:

outputBuffer_remake-2022-06-28

可以看到,直到執行到第二個getchar()時,屏幕上沒有新的輸出。

而在執行了printf("Print!")之後,輸出緩衝區被填滿了,輸出緩衝區中現有的10位元組的數據被寫入到stdout中,繼而才在屏幕上列印出123456789P

緩衝區內容被讀走後,剩餘的字元串rint!接著被寫入輸出緩衝區。程式運行結束後,輸出緩衝區中的內容會被全部列印到屏幕上,所以會在最後看到rint!

C語言中常用的輸入函數

輸入函數做的工作主要是從文件流中讀取數據,亦可將讀取到的數據儲存到記憶體中以供後續程式使用。

基於字元

// 從給定的文件流中讀一個字元 (fgetc中的 f 的意思即"function")
int fgetc( FILE *stream ); 

// 同fgetc,但是getc的實現*可能*是基於巨集的
int getc( FILE *stream ); 

// 相當於是getc(stdin),從標準輸入流讀取一個字元
int getchar(void);

// 返回獲取的字元的ASCII碼值,如果到達文件末尾就返回EOF(即返回-1)

基於行

// 從給定的文件流中讀取(count-1)個字元或者讀取直到遇到換行符或者EOF
// fgets中的f代表“file”,而s代表“string”
char *fgets( char *restrict str, int count, FILE *restrict stream );

// 返回指向字元串的指針或者空指針NULL

格式化輸入

// 按照format的格式從標準輸入流stdin中讀取所需的數據並儲存在相應的變數中
// scanf中的f代表“format”
int scanf( const char *restrict format, ... );

// 按照format的格式從文件流stream中讀取所需的數據並儲存在相應的變數中
// fscanf中前一個f代表“file(stream)”,後一個f代表“format”
int fscanf( FILE *restrict stream, const char *restrict format, ... );

// 按照format的格式從字元串buffer中截取所需的數據並儲存在相應的變數中
// sscanf中的第一個s代表“string”,字元串
int sscanf( const char *restrict buffer, const char *restrict format, ... );

// 返回一個整型數值,代表成功根據格式賦值的變數數(arguments)

最常到的輸入流問題

先來個不會出問題的示例:

#include <stdio.h>
int main()
{
	char test1[200];
	char test2[200];
	char testChar;
	printf("Input a Character: \n");
	testChar = getchar();
	fprintf(stdout, "Input String1: \n");
	scanf("%s", test1);
	fprintf(stdout, "Input String2: \n");
	scanf("%s", test2);
	printf("Got String1: [ %s ]\n", test1);
	printf("Got String2: [ %s ]\n", test2);
	printf("Got Char: [ %c ]\n", testChar);
	return 0;
}

運行效果:

correctExample-2022-06-28


出問題的示例:

#include <stdio.h>
int main()
{
	char test[200];
	char testChar1, testChar2, testChar3;
	fprintf(stdout, "Input String: \n");
	scanf("%3s", test);
	printf("[1]Input a Character: \n");
	testChar1 = getchar();
	printf("[2]Input a Character: \n");
	testChar2 = fgetc(stdin);
	printf("[3]Input a Character: \n");
	testChar3 = getchar();
	printf("Got String: [ %s ]\n", test);
	printf("Got Char1: [ %c ]\n", testChar1);
	printf("Got Char2: [ %c ]\n", testChar2);
	printf("Got Char3: [ %c ]\n", testChar3);
	return 0;
}

運行效果:

incorrectExample-2022-06-28

因為我將格式設置為了%3s,所以scanf最多接收包含三個字元的字元串。

在這個示例中,我按要求輸入了一條字元串Hello,並按下回車輸入一個換行符,緩衝區數據Hello\n被寫入到了stdin中。而scanf只從標準流stdin中讀走了Hel這一部分字元串。

此時,標準流stdin中實際上還剩3個字元:

  1. l
  2. o
  3. \n (回車輸入的換行符)

於是接下來三次針對字元的輸入函數只會分別stdin取走這三個字元,而不會等待用戶輸入,這就沒有達到我想要的效果。

在基本的命令行程式中很容易遇到這類問題,這也是為什麼需要及時清空輸入流stdin中的數據

如何處理殘餘內容


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在使用樹形節點或級聯組件時常常會碰到根據id處理數據的情況 下麵為大家簡單介紹關於節點遞歸增刪改查方法 根據目標id刪除指定節點 /** * 根據目標id刪除指定節點 * @param {*} list 數據源 * @param {*} targetId 目標id */ function delet ...
  • JavaScript進階知識點——函數和對象詳解 我們在上期內容中學習了JavaScript的基本知識點,今天讓我們更加深入地瞭解JavaScript JavaScript函數 JavaScript函數和Java函數是有一部分相似的,所以學習起來也會相對簡單 基本構造 1.直接構造 //functi ...
  • 當前工作區內的文件如下 首先當然是安裝 yarn,此命令可在任何地方執行 npm install -g yarn 查看版本,會發現版本並不是 3.因為版本 1 是經典版 升級 yarn,此命令在你的工作區根目錄運行 註意:這個升級操作不會影響你的其他項目 yarn set version berry ...
  • 剛學習完Java SE,不知道怎麼寫項目?不知道寫什麼項目?本篇文章將線上ATM詳細架構設計分享出來,幫助初學者開發項目。 ...
  • 原文鏈接:全網最新的nacos 2.1.0集群多節點部署教程-語雀 基本信息 進度整理中 版本 2.1.0 版本發佈日期 2022-04-29 git revision number b584531331cc95054964ba4e33984f4cab9e582d 編寫日期 2022-07-04~ ...
  • 苦惱於Python運行時感人的速度,我決定學習C++。 為了激勵我自己好好地學習這門未曾謀面的編程語言,我決定在此開設專欄:C++學習日記。希望在讀者們的監督下,我可以早日掌握這門語言。當然,如果那位大佬願意賜教,在下也是感激不盡。 2022年7月14日 由於懶得安裝編譯環境,我找了一個線上編程的網 ...
  • 面向對象編程(基礎) 類與對象 ●使用現有技術解決 張老太養了兩隻貓貓:一隻名字叫小白,今年3歲,白色。還有一隻叫小花,今年100歲,花色。請編寫一個程式,當用戶輸入小貓的名字時,就顯示該貓的名字,年齡,顏色。如果用戶輸入的小貓名錯誤,則顯示張老太沒有這隻貓貓。 1)單獨的定義變數解決 2)使用數組 ...
  • MyDisruptor V5版本介紹 在v4版本的MyDisruptor實現多線程生產者後。按照計劃,v5版本的MyDisruptor需要支持更便於用戶使用的DSL風格的API。 由於該文屬於系列博客的一部分,需要先對之前的博客內容有所瞭解才能更好地理解本篇博客 v1版本博客:從零開始實現lmax- ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...