C++輸入輸出流 cin/cout 及格式化輸出簡介


  C++ 可通過流的概念進行程序與外界環境( 用戶、文件等 )之間的交互。流是一種將數據自源( source )推送至目的地( destination )的管道。在 C++ 中,與標准輸入/輸出相關的流可通過頭文件 <iostream> 使用,與文件讀寫相關的流可以通過頭文件 <fstream> 使用。這里即主要介紹 C++ 中與標准輸入/輸出相關的流 cin / cout .

 

頭文件與命令空間

  引入頭文件

  在 C++ 中,想要使用相應的標准庫功能,需要包括對應的庫的頭文件。故而想要使用標准輸入/輸出,程序中首先需要包含對應的頭文件 iostream.

    #include <iostream>        //包含對應的頭文件

  iostream 頭文件中主要包括 4 個流對象的聲明( 注意是對象而不是類型,實際是這些流對象的 extern 聲明 )。在包含 iosteam 頭文件后,用戶可以直接使用標准輸入流 cin、標准輸出流 cout 、標准錯誤流 cerr 和標准日志流 clog. 其中 cin 為輸入流對象,后三個為輸出流對象。上述流對象在標准庫中已經定義,故而后續用戶在程序中可以直接使用上述對象進行操作而不需要另行定義。這里主要介紹的是標准輸入/輸出流( 即 cin / cout )的使用。

  引入名字

  C++ 中使用命名空間的概念加強程序的作用域和命名管理能力,標准庫定義的所有名字都在命令空間 std 中( 只有聲明命名空間后才能使用 )。若在程序中直接使用 cin 和 cout 進行輸入輸出操作而不包含對應的命名空間聲明,在編譯過程中會因無法找到對應的名字而報錯。C++ 中使用 cin / cout 時有以下幾種方法。

  1.在程序開始處( 頭文件之后 )直接引入整個命名空間中的名字。所有 C++ 標准庫的名字均位於命名空間 std 中,包括 cin 和 cout 。如下所示的語句即導入了命名空間 std 中的所有名字。則后續程序中可以直接使用命名空間 std 中所定義的名字,包括可以直接使用 cin 和 cout 進行輸入/輸出操作。直接導入整個命名空間中的所有名字書寫起來十分簡單,但在較復雜的程序中可能需要解決由於所有命名空間中名字的引入所產生的與其他已有名字的沖突問題。這種方法推薦在較為簡單的程序設計/練習中使用。

    using namespace std;     //使得命令空間 std 中的名字均可直接使用

  2.在程序開始處( 頭文件之后 )一次引入一個命名空間中的一個成員。通過 using 關鍵字 + 命名空間 :: 成員名字 的方式一次引入一個指定命名空間中的成員。若后續程序中需要同時使用標准輸入和輸出流,則需要分別引入這兩個名字,如下所示。注意在這種方法中,每引入一個名字,均需要一條單獨的語句。 

    using std::cin;    //引入命名空間 std 中的名字 cin,后續可以直接使用名字 cin 進行操作
    using std::cout;    //引入命名空間 std 中的名字 cout,后續可以直接使用名字 cout 進行操作

  3.直接在程序中通過 命名空間 :: 成員名字 的方式去引用某個名字,而不需做額外的聲明。如在程序中需要使用 cin,則可以直接使用如下語句。其缺點在於程序中每一次使用 cin ,均要通過 std::cin 的方式進行使用。

    std::cin >> value ;            //直接通過 命名空間::名字 的方式使用 cin 

  后續的示例中,均默認已經導入了對應的頭文件並使用上述方法2的方式引入了對應的名字,后續不再重復。

 

基本輸入/輸出操作

  輸入/輸出運算符

  與流相關的輸入輸出操作通過操作符 "<<" 和 ">>" 完成。

  >> : 輸入運算符,其左側應該為輸出流( ostream )類型的對象,右側為需讀取的數據,運算符的操作結果( 可理解為語句的返回值 )為左側的流對象;

  << : 輸出運算符,其左側應該為輸入流( istream )類型的對象,右側為需輸出的數據,運算符的操作結果( 可理解為語句的返回值 )為左側的流對象;

  這里需要說明的是,cin 實際上即為 istream 類型的對象,而 cout 即為 ostream 類型的對象。故而 cin 和 cout 可以直接通過上述的輸入/輸出運算符進行輸入/輸出操作。cin/cout 的類型關系可以參考這里。  

  

  基本運算

  通過輸入/輸出運算符可以進行基本的輸入/輸出操作,cin / cout 可自動對 C++ 的基本數據類型如數值類型、字符(串)類型進行支持。下列示例即讀入一個整型數據,並將其輸出。這里注意,代碼中的 endl 並不代表實際的數據,而是起到結束該行( 從而換行 )的作用.

    int value ;          //存放數據的整形
    cin >> value ;        //從標准輸入讀取一個整型,並存儲在變量 value 中,輸入使用 cin 和 >> 運算符
    cout << value << endl ;  //將 value 的值輸出至標准輸出,並結束該行,輸出使用 cout 對象和 << 運算符

  輸入/輸出運算符也可以進行連續的輸入/輸出操作。以輸入為例,可通過如下語句讀取兩個輸入整型。  

    int a, b;
    cin >> a >> b;    //將讀取的數據分別存儲在 a 和 b 中

  這里主要解釋第二行的語句。語句由左向右執行,首先執行 "cin >> a" 部分,讀取一個整型數據至變量 a 中,由於 ">>" 運算符的返回值為其左側對象,即返回 cin ,則后續繼續執行的語句等價於 "cin >> b",即讀取另外一個整型數據至變量 b 中。 

  使用 cin 讀取鍵盤輸入時,其與 c 中實現的 scanf 類似,一般采用行緩沖的方式處理用戶數據。當緩沖區 stdin 中存在數據時,cin 直接從 stdin 中讀取數據,而當 stdin 中沒有數據時,cin 即等待用戶的輸入。具體而言,用戶的鍵盤的輸入首先被存儲在臨時緩沖區中,當臨時緩沖區滿或用戶輸入回車鍵,則上述輸入數據被緩沖至 stdin 中,cin 即可從 stdin 中獲取數據,未被讀取的數據會一直存放在該緩沖區中,在下一次 cin 讀取時,會直接從上述非空的 stdin 中讀取數據,而不再等待用戶輸入。當 stdin 為空時,cin 會再次等待用戶的輸入。

  另注,cin 在讀取目標數據時,會自動略過有效數據之前的空白字符( 回車、換行、制表符等 )。如用戶輸入為 "       32",通過 cin 讀取到的整形為 32.在讀取有效數據時,遇到第一個空白字符即認為當前讀取的數據結束,如用戶輸入"32          ",cin 在讀取數據 32 后遇到空格,即認為當前輸入的整型數據為 32 。

 

  輸入錯誤處理

   在類型匹配的情況下,cin 可通過 ">>" 運算符方便地針對不同的數據類型進行輸入操作。如上面的例子中,cin 可以將用戶輸入的整型數據提取到整型變量 value 中。但當出現 a)數據類型不匹配,b)數據讀取至結束位置( 用戶輸入的結束標記時 ) ,cin 會進入錯誤的狀態。如程序中需要通過語句 cin >> value 將數據讀取至整型的變量 value 中,而用戶實際輸入為一個字符串"abc"時,會產生輸入錯誤。具體而言,會有以下幾個問題:  

  1.使用的輸入流 cin 會進入錯誤狀態,流的錯誤狀態可以通過 fail() 方法進行檢測。當流進入錯誤狀態時,cin.fail() 返回值為 true .

    int value;
    cin >> value;
    if( !cin.fail() )         //確保讀取過程正確,再進行輸出
    {
        cout << value << endl; //output        
    }            

  流對象本身也可以用作判斷條件,當 cin 成功讀取數據時,cin 在判斷條件中表現為 true,當 cin 進入錯誤狀態時,其在判斷條件中表現為 false .例如,通過如下語句讀取用戶輸入的任意多的數據.只要用戶輸入合法的整型值,cin 即可正確讀取,>> 運算符返回的是其左側的對象,也就是 cin .成功讀取的 cin 在條件判斷中的表現為 true,則可以進一步進行處理操作。當用戶在終端中輸入結束標記時( Linux 下的 Ctrl + d , Windows 下的 Ctrl + z 加回車 ),cin 即進入錯誤狀態,從而使得 while 循環的條件為假,程序不再進行后續操作。

    int value;
    while( cin >> value )    
    {
        //process value
    }

  2.處於錯誤狀態的流對象( 這里即 cin )會使得后續使用該流的語句的結果均無法預料。除對流對象狀態的檢測外,若后續程序流程中仍然使用了同一個流對象,則程序首先需對錯誤進行處理( 報告錯誤、清空緩沖區數據等 ),之后通過 clear 方法將流對象恢復至正常狀態,后續即可繼續使用。注意 clear 方法並不會清除緩沖區中的數據。如例子中,由於 "abc" 和整型不匹配的錯誤所造成的問題,字符串"abc"仍留在緩沖區中,若不做任何處理直接通過 cin.clear() 恢復流的狀態,后續通過 cin 的讀取會直接讀取緩沖區中的 "abc" ,進而影響后續的程序執行流程.

 

格式化輸出

  與 C 語言提供的輸出函數類似,除基本的數據交互功能外,C++ 的輸出流還可通過輸入/輸出操作符( input/output manipulators )來滿足數據輸出的格式化需求。

  結束當前行——endl

  默認情況下,將數據輸出時並不會自動換行,需要顯式的通過 endl 來進行數據的換行。輸出 endl 的效果是結束當前行( 也就是換行),並將設備管理的緩沖區中的內容刷新到設備中( 也就是保證數據真正的輸出了,而不是停留在內存等緩沖位置中 )。 另外轉義字符 '\n' 也可以滿足換行的功能。下列代碼均在數據輸出后進行了換行,不同的是 endl 會保證將數據刷新到目的設備中( 而不是暫存在緩沖區中 )。

    cout << "hello world!" << '\n';
    cout << "hello world!" << endl;

 

  設置輸出間距( 寬度 )——setw

  需包含頭文件 <iomanip> . setw 函數以一個整型( int )作為參數,設置進行輸出時的流的寬度。默認情況下,使用流進行輸出操作時流的寬度為0,亦即保持數據的原始長度。使用 setw( n ) 設置流寬度后,輸出的數據至少會占有 setw( n ) 所設置的字節數 n。具體而言,當數據寬度不足 n 時,即通過填充( 默認使用空格填充 )使其寬度保持為 n 。注意,setw 函數的設置在調用 "<<" 或 ">>" 運算符后即恢復默認,也就是僅在其設置后的下一次輸出操作中起作用。( setw 可設置輸出流進行輸出的數據的最小字節寬度,也可在輸入流中設置進行輸入的最大字節寬度 )

    int value = 45;
    cout << setfill( 'c' ) << setw( 10 ) << value << endl;    //輸出的 45 將占用 10 個字節的寬度,使用 'c' 填充    =>  "cccccccc32"

 

  設置默認填充字符——setfill

  需包含頭文件 <iomanip> . setfill 函數僅用於輸出流中,其使用一個字符作為參數,並將其設置為輸出流的填充字符( 默認為空格 )。在調用了 setfill 函數后,后續所有需要進行填充的場合( 如使用 setw 設置了輸出的最小字節寬度 )均使用設置的字符進行填充。與 setw 函數不同的是,setfill 在設置完成后一直有效,除非重新設置。

    cout << setfill( '-' ) << setw( 10 ) << "" << endl;    //輸出空字符串,由於輸出的最小寬度經過 setw 設置為 10 ,故輸出連續十個 ‘-’   =>  "----------"
    cout << setfill( ' ' )                       //恢復填充為空格

 

  設置填充字符位置——left / right

  設置經過填充后,原始數據的位置。設置為 left / right 時,原始數據位於填充后數據的左/右側( 默認輸出為 right ).  

    int value = 32;
    cout << left << setfill( 'c' ) << setw( 10 ) << value << endl;    //輸出寬度為 10,數據位於輸出的左側       =>    "32cccccccc"

 

  參考 :

  Input/Output —— cppreference.com

  Input/output manipulators —— cppreference.com


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM