七、輸入/輸出流

流類簡介

C++中凡是數據從一個地方傳輸到另一個地方的操作都是的操作。因此,一般意義下的讀操作在流數據抽象中被稱為(從流中)“提取”,寫操作被稱為(向流中)“插入”。

iostream流類庫的類關系圖如下:

流類庫關系圖

圖中箭頭代表派生關系。ios是抽象基類,提供輸入/輸出所需的公共操作,它派生出兩個類istreamostream。為了避免多重繼承的二義性,從ios派生istreamostream時,均使用了virtual關鍵字(虛繼承)

stream類提供了流的大部分輸入操作,對系統預定義的所有輸入流重載提取運算符>>ostream類對系統預定義的所有輸出流重載插入運算符<<

C++的iostream類庫提供了數百種I/O功能,iostream類庫的接口部分包含在幾個頭文件中。常見的頭文件有以下3個:

  1. iostream:包含操作所有輸入/輸出流所需要的基本信息,因此大多數C++程序都應包含這個頭文件。該文件含有4個標準流對象,提供了無格式化和格式化的I/O功能。
  2. iomanip:包含格式化I/O的帶參數流操縱符,可用于指定數據輸入/輸出的格式。
  3. fstream:包含處理文件的有關信息,提供建立文件、讀/寫文件的各種操作接口。

標準流對象

C++在頭文件iostream中為了用戶預定義了4個標準流對象,分別是:

  • cin(標準輸入流),cin與標準輸入設備(鍵盤)相關聯,用于讀取數據,可以被重定向為從文件中讀取數據。
  • cout(標準輸出流),cout與標準輸出設備(顯示器)相關聯,用于輸出數據,可以被重定向為向文件里寫入數據。
  • cerr(非緩沖錯誤輸出流),cerr與標準錯誤信息輸出設備(顯示器)相關聯(非緩沖),用于輸出出錯信息,不能被重定向。
  • clog(緩沖錯誤輸出流),clog與標準錯誤信息輸出設備相關聯(緩沖),用于輸出出錯信息,不能被重定向。

在實際中,cin常用于從鍵盤輸入數據,是流類istream的對象。cout常用于向屏幕輸出數據,是流類ostream的對象。

#include <iostream>
using namespace std;

int main() {
    int x, y;
    cin >> x >> y;
    //函數`freopen()`的功能是將`stream`按`mode`指定的模式重定向到路徑`path`指向的文件。
    //將標準輸出定向到文件test.txt
    freopen("test.txt", "w", stdout);
    
    if (y == 0) {
        cerr << "error" << endl;
    } else {
        cout << x << " / " << y << " = " << x / y << endl;
    }

    return 0;
};

控制I/O格式

C++進行I/O格式控制的方式一般有使用流操縱符、設置標志字和調用成員函數。

流操縱符

流操縱符 作用
endl 輸出一個新行符,并清空流
ends 輸出字符串結束,并清空流
flush 清空流緩沖區
dec*(默認) 以十進制形式輸入/輸出整數
hex 以十六進制形式輸入或輸出整數
cot 以八進制形式輸入或輸出整數
ws 提取空白字符
fixed 以普通小數形式輸出浮點數
scientific 以科學計數法形式輸出浮點數
left 左對齊,即在寬度不足時將填充字符添加到右邊
right* 右對齊,即在寬度不足時將填充字符添加到左邊
setbase(int b) 設置輸出整數時的進制,b為8、10、16
setw(int w) 指定輸出寬度為w個字符,或輸入字符串時讀入w個字符。
一次有效。
setfill(int c) 在指定輸出寬度的情況下,
輸出的寬度不足時用ASCⅡ碼為c的字符填充(默認情況是用空格填充)
setprecision(int n) 設置輸出浮點數的精度為n
在使用非fixed且非scientific方式輸出的情況下,
n即為有效數字最多的位數。
如果有效數字位數超過n
則小數部分四舍五入,
或自動變為科學計數法輸出并保留一共n位有效數字;
在使用fixed方式和scientific方式輸出的情況下,
n是小數點后面應保留的位數。
setiosflags(fmtflags f) 通用操縱符。將格式標志f所對應的格式標志位置為1
resetiosflags(fmtflags f) 通用操縱符。將格式標志f所對應的格式標志位置為0(清除)
boolapha turefalse輸出為字符串
noboolapha* turefalse輸出為10
showbase 輸出表示數值進制的前綴
noshowbase* 不輸出表示數值進制的前綴
showpoint 總是輸出小數點
noshowpoint* 只有當小數部分存在時才顯示小數點
showpos 在非負數值中顯示+
noshowpos* 在非負數值中不顯示+
#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    int n = 65535, m = 20;
    
    //1.分別輸出一個整數的十進制、十六進制和八進制表示
    cout << "1)" << n << " = " << hex << n << " = " << oct << n << endl;
    //1)65535 = ffff = 177777
    
    //2.使用setbase分別輸出一個整數的十進制、十六進制和八進制表示
    cout << "2)" << setbase(10) << m << " = " << setbase(16) << m << " = " << setbase(8) << m << endl;
    //2)20 = 14 = 24
    
    //3.使用showbase和setbase分別輸出一個整數的十進制、十六進制和八進制表示
    cout << "3)" << showbase;//輸出表示數值進制的前綴
    cout << setbase(10) << m << " = " << setbase(16) << m << " = " << setbase(8) << m << endl;
    //3)20 = 0x14 = 024

    return 0;
};

標志字

標志常量名 含義 輸入/輸出
ios::skipws 0x0001 跳過輸入中的空白 I
ios::left 0x0002 按輸出域左對齊,用填充字符填充右邊 O
ios::right* 0x0004 按輸入域右對齊,用填充字符填充做左邊 O
ios::internal 0x0008 在符號位或基數指示符后填入字符 O
ios::dec* 0x0010 轉換為十進制基數形式 I/O
ios::oct 0x0020 轉換為八進制基數形式 I/O
ios::hex 0x0040 轉換為十六進制基數形式 I/O
ios::showbase 0x0080 在輸出中顯示基數指示符 O
ios::showpoint 0x0100 在輸出浮點數時必須帶小數點和尾部的0 O
ios::uppercase 0x0200 以大寫字母表示十六進制數,科學計數法使用大寫字母 O
ios::showpos 0x0400 正數前面加+ O
ios::scientific 0x0800 科學計數法顯示浮點數 O
#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    double x = 12.34;
    
    cout << "1)" << setiosflags(ios::scientific | ios::showpos) << x << endl;
    cout << "2)" << setiosflags(ios::fixed) << x << endl;
    cout << "3)" << resetiosflags(ios::fixed) << setiosflags(ios::scientific | ios::showpos) << x << end;
    cout << "4)" << resetiosflags(ios::showpos) << x << endl;
    //1)+1.234000e+001
    //2)+12.34
    //3)+1.234000e+001
    //4)1.234000e+001

    return 0;
};

調用cout的成員函數

成員函數 作用相同的流操作符
precision(int np) setprecision(np)
width(int nw) setw(nw)
fill(char cFill) setfill(cFill)
setf(long iFlags) setiosflags(iFlags)
unsetf(long iFlags) resetiosflags(iFlags)
#include <iostream>
using namespace std;

int main(){
    double values[] = {
        1.23,
        20.3456,
        300.4567,
        4000.45678,
        50000.1234567
    };
    
    cout.fill('*');//設置填充字符為`*`
    for (int i = 0; i < sizeof(values) / sizeof(double); i++) {
        cout << "values[" << i << "] = (";
        cout.width(10);//設置輸出寬度
        cout << values[i] << ")" << endl;
    }
    /*
    values[0] = (******1.23)
    values[1] = (***20.3456)
    values[2] = (***300.457)
    values[3] = (***4000.46)
    values[4] = (***50000.1)
    */
    
    cout.fill(' ');//設置填充字符為空格
    for (int j = 0; j < sizefo(values) / sizeof(double); j++) {
        cout << "values[" << j << "] = (" 
        cout.width(10);//設置輸出寬度
        cout.precision(j + 3);//設置保留有效數字
        cout << values[j] << ")" << endl;
    }
    /*
    values[0] = (     1.23)
    values[1] = (    20.35)
    values[2] = (   300.46)
    values[3] = (  4000.46)
    values[4] = ( 50000.12)
    */
    
    return 0;
};

調用cin的成員函數

istream類提供了一些公有成員函數,它們可以以不同的方式提取輸入流中的數據。

get()函數

#include <iostream>
using namespace std;
 
int main() {
    int n = 0;
    char ch;
    //當文件沒有結束時繼續進行循環
    while ((ch = cin.get()) != EOF) {
        cout.put(ch);
        n++;
    }
    cout << "輸入字符共計:" << n << endl;
};

Windows環境下,當進行鍵盤輸入時,在單獨的一行按ctrl + z組合鍵后再按enter鍵就代表文件輸入結束。

getline()函數

getline()成員函數的原型如下:

istream & getline(char *buf, int bufSize);

其功能是從輸入流中的當前字符開始讀取bufSize - 1個字符到緩沖區buf,或讀到\n為止(哪個條件先滿足即按哪個執行)。函數會在buf中讀入數據的結尾自動添加串結束標記\0

istream & getline(char *buf, int bufSize, char delim);

其功能是從輸入流中的當前字符開始讀取bufSize - 1個字符到緩沖區buf,或讀到字符delim為止(哪個條件先滿足即按哪個執行)。函數會在buf中讀入數據的結尾自動添加\0

兩者的區別在于,前者是讀到\n為止,后者是讀到指定字符delim為止。字符\ndelim都不會被存入buf中,但會從輸入流中取走。

函數getline()的返回值是函數所作用的對象的引用。如果輸入流中\ndelim之前的字符個數達到或超過bufSize,則會導致讀入操作出錯,其結果是:雖然本次讀入已經完成,但是之后的讀入都會失敗。

#include <iostream>
using namespace std;

int main() {
    char buf[10];
    int i = 0;
    //若輸入流的一行超過9個字符,則會出錯
    while (cin.getline(buf, 10)) {
        cout << ++i << ":" << buf << endl;
    }
    cout << "last:" << buf << endl;
    
    return 0;
};

eof()函數

eof()成員函數的原型如下:

bool eof();

eof()函數用于判斷輸入流是否已經結束。返回值為true表示輸入結束。

在應用程序中可以用eof()函數測試是否到達文件尾,當文件操作結束遇到文件尾時,函數返回1;否則返回0

ignore()函數

ignore()成員函數的原型如下:

istream & ignore(int n = 1, int delim = EOF);

此函數的作用是跳過輸入流中的n個字符,或跳過delim及其之前的所有字符(哪個條件先滿足就按哪個執行)。兩個參數都有默認值。因此cin.ignore()等效于cin.ignore(1, EOF),即跳過一個字符。該函數常用于跳過輸入中的無用部分,以便提取有用的部分。

#include <iostream>
using namespace std;

int main() {
    char str[30];
    while (!cin.eof()) {
        cin.ignore(10, ':');
        if (!cin.eof()) {
            cin >> str;
            cout << str << endl;
        }
    }

    return 0;
};

peek()函數

peek()成員函數的原型如下:

int peek();

函數peek()返回輸入流中的當前字符,但是并不將該字符從輸入流中取走,相當于只是“看了一眼”將要讀入的下一個字符,因此叫“窺視”。

cin.peek()不會跳過輸入流中的空格和回車符。在輸入流已經結束的情況下,cin.peek()返回EOF

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372