C++ Primer第五版筆記——特殊的IO庫特性(一)

格式化輸入與輸出

除了條件狀態外,每個iostream對象還維護一個格式狀態來控制IO如何格式化的細節,比如控制整數的進制、浮點數的精度、輸出寬度等。
標準庫定義了一組操縱符來改變流的格式狀態,一個常用的操縱符:endl,一般將它“寫”到輸出流,就像它是一個值一樣,但它不是一個普通值,而是一個操作:它輸出一個換行符並刷新緩衝區。

很多操縱符改變格式狀態
操作符用於兩大類輸出控制:控制數值的輸出形式以及控制補白的數量和位置
大多數改變格式狀態的操縱符都是設置/復原成對的:一個操縱符用來將格式狀態設置爲一個新值,另一個操縱符將其復原。
當操縱符改變流的格式狀態後,通常改變後的狀態對所有後續的IO都生效。

控制布爾值的格式
默認情況下,bool值打印爲1或0,一個true值輸出爲整數1,一個false值輸出爲整數0。可以通過對流使用boolalpha操縱符來覆蓋這種模式:

cout<<<<true<<" "<<false
    <<"\n"<<boolalpha<<true<<" "<<false<<endl;
//執行以上代碼後的輸出如下:
1 0
true false

一旦向cout“寫入” boolalpha,就改變了cout打印bool值的方式,後續的布爾值打印操作都會打印true和false而不是1和0.
爲了取消cout格式的改變,可以使用noboolalpha:

cout<<boolalpha<< true <<" "<<noboolalpha<<false <<endl;

//執行以上代碼將會有如下輸出:
true 0

指定整型值的進制
默認情況下,輸出整型是十進制,可以通過操縱符hex、oct和dec將其改爲十六進制、八進制或者改回十進制:

    cout<<"default:"<<15<<endl;
    cout<<"hex:"<<hex<<15<<endl;
    cout<<"oct:"<<oct<<15<<endl;
    cout<<"dec:"<<dec<<15<<endl;
//輸出如下:
default15
hex:f
oct:17
dec:15

在輸出中指出進制
默認情況下,打印出來的數值並看不出使用的是幾進制,當使用操縱符showbase時,會在輸出結果中顯示進制:
前導0x表示十六進制
前導0表示八進制
沒有前導表示十進制。

    cout<<"default:"<<showbase<<15<<endl;
    cout<<"hex:"<<hex<<15<<endl;
    cout<<"oct:"<<oct<<15<<endl;
    cout<<"dec:"<<dec<<15<<endl;
    cout<<noshowbase<<endl;    //恢復流狀態
//輸出將如下:
default15
hex:0xf
oct:017
dec:15

控制浮點數格式
可以控制浮點數輸出三種格式:
1.以多高精度打印浮點數;
2.數值是打印爲十六進制、十進制還是科學技術法形式;
3.對於沒有小數部分的浮點值是否打印小數點。
默認情況下,浮點值按照六位數字精度打印;如果浮點值沒有小數部分,則不打印小數點;會根據浮點數的值選擇打印爲十進制或是科學計數法形式(非常大或非常小的值會打印爲科學計數法形式,其他值打印爲定點十進制形式)。

指定打印精度
默認情況下,精度控制打印的數字的總數,當打印時,浮點數按當前精度舍入。可以通過調用IO對象的precision成員或setprecision操縱符來改變精度:

    cout<<"precision:"<<cout.precision()<<",value:"<<sqrt(2.0)<<endl;
    cout.precision(12);
    cout<<"precision:"<<cout.precision()<<",value:"<<sqrt(2.0)<<endl;
    cout<<setprecision(3);
    cout<<"precision:"<<cout.precision()<<",value:"<<sqrt(2.0)<<endl;

//輸出如下:
precision:6,value:1.41421
precision:12,value:1.41421356237
precision:3,value:1.41

其他操作符
這裏寫圖片描述
這裏寫圖片描述

這裏寫圖片描述


未格式化的輸入輸出

標準庫提供了一組底層操作,來支持未格式化IO。這些操作允許將一個流當做無解釋的字節序列來處理。

單字節操作
有幾個未格式化操作每次操作一個字節地處理流,這些操作會讀取空格而不是忽略。例如使用未格式化操作get和put來讀取和寫入一個字符:

char ch;
while(cin.get(ch)){
    cout.put(ch);
}

以上程序的輸入輸出完全相同
這裏寫圖片描述

將字符放回輸入流
有時需要讀取一個字符才知道還未準備好處理它,這時需要將字符放回流中,標準庫中有三種退回字符的方法:
1.peek:返回輸入流中的下一個字符的副本,但不會將它從流中刪除,peek返回的值仍然留在流中;
2.unget:使得輸入流向後移動,從而最後讀取的值又回到流中,即使不知道最後從流中讀取什麼值,仍然可以調用unget;
3.putback:一個更爲特殊的unget,他退回從流中讀取的最後一個值,但它接受一個參數,此參數必須與最後讀取的值相同。

從輸入操作返回的是int值
函數peek和無參的unget版本都以int類型從輸入流中返回一個字符,這裏是返回int而不是返回char的原因是:返回的int值還可以返回文件尾標記,而char範圍的每個值都只能表示一個真實的字符,沒有額外的值來表示文件尾。
返回int的函數將它們要返回的字符先轉換成unsigned char,然後再將結果提升到int。因此,即使字符集中有字符映射到負數,這些操作返回的值也是正值,而標準庫使用負值來表示文件尾。頭文件cstdio中定義了一個名爲EOF的const,可用來檢測是否到了文件尾:

//這個ch是定義的int類型,這很重要
int ch;
while((ch = cin.get()) != EOF){
    cout.put(ch);
}

多字節操作
一些未格式化IO操作一次處理大塊數據。這些操作除了容易出錯外,還要求程序員來分配並管理用來保存和提取數據的字符數組。
這裏寫圖片描述
這裏寫圖片描述

get和getline兩個函數的行爲類似但不完全相同:兩個函數的差別是處理分隔符的方式:get將分隔符留作istream中的下一個字符,而getline則讀取並丟棄分隔符,無論哪個函數都不會將分隔符保存在用來保存數據的數組中。

確定讀取了多少個字符
某些操作從輸入讀取未知個數的字節,可以使用gcount來確定最後一個未格式化操作從輸入讀取了多少個字符。應該在任何後續未格式化輸入操作之前調用gcount。特別是,將字符退回流中的操作也是未格式化輸入操作,如果使用了peek、unget或putback後調用gcount,則gcount的返回值將爲0。

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