C++ 流的操作 | 初識IO類、文件流、string流的使用

文章目錄

  • 前言
  • IO頭文件
    • iostream
    • fstream
    • sstream
  • 流的使用
    • 不能拷貝或對 IO對象 賦值
    • 條件狀態與 iostate 類型
  • 輸出緩沖區
  • 文件流
    • fstream類型
    • 文件模式
    • 文件光標函數
      • tellg() / tellp()
      • seekg() / seekp()
    • 向文件存儲內容/讀取文件內容
  • string流
    • istringstream
    • ostringstream


前言

我們在使用 C++ 的過程中,總避免不了 IO操作,比如經常用到的一些 IO庫設施

  • istream:(輸入流)類型,提供輸入操作。
  • ostream:(輸出流)類型,提供輸出操作。
  • cin:一個 istream 對象,從標準輸入讀取數據。
  • cout:一個 ostream 對象,向標準輸出寫入數據。
  • cerr:一個 ostream 對象,通常用于輸出程序錯誤消息,寫入到標準錯誤。
  • >>運算符:用來從一個 istream 對象讀取輸入數據。
  • <<運算符:用來向一個 ostream 對象寫入輸出數據。
  • getline函數:從一個給定的 istream 讀取一行數據,存入一個給定的 string 對象中。

但實際上可能僅僅是懵懵懂懂在使用,如果不深入了解的話,這樣的使用是淺薄的。


IO頭文件

iostream

定義了用于讀寫的基本類型。

  • istream,wistream 從流讀取數據
  • ostream,wostream 向流寫入數據
  • iostream,wiostream 讀寫流

fstream

定義了讀寫命名文件的類型。

  • ifstream,wifstream 從文件讀取數據
  • ofstream,wofstream 向文件寫入數據
  • fstream,wfstream 讀寫文件

sstream

定義了讀寫內存string對象的類型。

  • istringstream,wistringstream 從 string 讀取數據
  • ostringstream,wostringstream 向 string 寫入數據
  • stringstream,wstringstream 讀寫 string

流的使用

標準庫通過繼承使我們忽略不同類型流之間的差異。舉例來說,類型 ifstreamistringstream 都繼承自 istream。因此,我們如何使用 cin ,就可以同樣地使用這些類型的對象。

不能拷貝或對 IO對象 賦值

ofstream out1, out2;
out1 = out2; // error:不能對流對象賦值
ofstream printf(ofstream); // error: 不能初始化ofstream參數
out2 = printf(out2); // error: 不能拷貝流對象
  • 由于不能拷貝IO對象,因此我們也不能將形參返回類型設置為流類型。
  • 進行IO操作的函數通常以引用方式傳遞和返回流。讀寫一個IO對象會改變其狀態,因此傳遞和返回的引用不能是const的。

條件狀態與 iostate 類型

IO操作使用不當的話會發生錯誤,而如果是發生在系統深處的錯誤,那么就超出了應用程序可以修正的范圍。但也有一些錯誤是可以恢復的,IO類也提供了一些函數和標志來訪問、操縱流的條件狀態

在這里插入圖片描述
在這里插入圖片描述

下面對表中的四個條件位作進一步介紹。

iostate 類型

IO庫定義了一個與機器無關的 iostate 類型,它提供了表達流狀態功能。

IO庫定義了 4個 iostate類型constexpr 值(常量表達式),表示特定的位模式

  • badbit: 表示系統級錯誤,如不可恢復的讀寫錯誤。通常情況下,一旦 badbit 被置位,流就無法再使用了。
  • failbit: 在發生可恢復錯誤后被置位,如期望讀取數值卻讀出一個字符等錯誤。這種問題通常是可以修正的,流還可以繼續使用。
  • eofbit: 如果到達文件結束位置,連同 failbit 一起被置位。
  • goodbit: 值為 0 時,表示流未發生錯誤。

對他們進行一個簡單的使用:

auto old_state = cin.rdstate(); // 返回流cin的當前狀態,返回值類型為 strm::iostate
cin.clear(); // 將cin所有條件位復位,換言之,使cin有效
// clear重載版本允許有參數,接受一個iostate值,表示流的新狀態
cin.clear(cin.rdstate() & ~cin.failbit & ~cin.badbit); 
// 先用rdstate讀出當前條件狀態,再將failbit和badbit復位生成新狀態。
process_input(cin); // 使用cin
cin.setstate(old_state); // 將cin置為原有狀態

輸出緩沖區

以前對于輸出緩沖區是沒什么概念的……直到在做美團往年筆試題的時候,有道編程題如果用 endl 作為換行會報超時,原因是 endl 頻繁刷新輸出緩沖區,因此需要用 '\n'

操作系統的 IO操作 是很耗時的,緩沖機制使操作系統將程序的多個輸出操作組合成單一的系統級寫操作(寫到顯示設備上),對性能的提升是巨大的。

導致緩沖刷新(數據真正寫到輸出設備或文件)的原因有很多:

  1. 程序正常結束,作為 main函數return操作 的一部分,緩沖刷新被執行。
  2. 緩沖區滿時,需要刷新緩沖,而后新的數據才能繼續寫入緩沖區。
  3. 我們可以使用操縱符(如 endl) 來顯式刷新緩沖區。
  4. 在每個輸出操作之后,我們可以用 操縱符unitbuf 設置流的內部狀態,來清空緩沖區。默認情況下,對 cerr 是設置 unitbuf 的,因此寫到 cerr 的內容都是立即刷新的。
  5. 一個輸出流可能被關聯到另一個流。在這種情況下,當讀寫被關聯的流時,關聯到的流的緩沖區會被刷新。例如,默認情況下,cincerr 都關聯到 cout。因此,讀 cin 或寫 cerr 都會導致 cout 的緩沖區被刷新。

關于第三點,共有三種操作符可用來刷新緩沖:

  • endl: 換行并刷新緩沖區
  • flush: 僅刷新緩沖區,但不輸出任何額外字符
  • ends: 向緩沖區插入一個空字符然后刷新緩沖區

關于第四點:

  • unitbuf操縱符: 每次寫操作之后都進行一次 flush 操作
  • nounitbuf操縱符: 重置流,使其恢復默認的緩沖區刷新機制

值得一提的是,如果程序崩潰,輸出緩沖區不會被刷新。

關于第五點,C++提供了 tie函數 來查看當前對象關聯的輸入輸出流,tie 有兩個重載版本:

  • 無參數版本: 返回指向輸出流的指針。當前對象若關聯了一個輸出流,則返回指向該流的指針;若未關聯流,則返回空指針
  • 參數為一個指向 ostream 的指針: 將當前對象關聯到此 ostream

每個流同時最多關聯到一個流,但多個流可以同時關聯到同一個 ostream

示例:

cin.tie(&cout); // 標準庫默認將 cin 和 cout 關聯在一起
cin.tie() = cin.tie(nullptr); // 通過傳遞空指針,讓 cin 不再與其他流關聯

文件流

fstream類型

除了繼承自 iostream類型 的行為外,fstream類型 還增加了一些新的成員來管理與流關聯的文件。
在這里插入圖片描述

open函數

fstrm(s) 之所以能在調用時打開文件s,是因為自動調用了 open函數 ,等價于:

ifstream in; // 輸入文件流未與任何文件關聯
in.open(ifile); // 打開指定文件,并與in綁定

對一個已經打開的文件流調用 open 會失敗,此時 failbit 會被置位,隨后使用文件流的操作都會失敗。因此,調用 open 后檢測是否成功是個好習慣:

if(in) // 成功
else // 不成功

如果想要將文件流關聯到另一個文件,必須先關閉已關聯的文件:

in.cloes();
in.open(ifile);

open 成功調用會將 good() 設為 true

close函數

當一個 fstream對象 被銷毀時,close 會自動被調用。


文件模式

在這里插入圖片描述

每個文件流類型都定義了一個默認的文件模式,當我們未指定文件模式時,就使用此默認模式。

  • ifstream關聯 的文件默認以 in模式 打開;
  • ofstream關聯 的文件默認以 out模式 打開;
  • fstream關聯 的文件默認以 in和out模式 打開。

雖然不論是調用 open 打開文件,還是 fstrm(s) 這樣隱式打開文件,都可以指定文件模式,但指定文件模式有如下限制:

  1. 只可以對 ofstreamfstream 對象設定 out 模式。
  2. 只可以對 ifstreamfstream 對象設定 in 模式。
  3. 只有 out 也被設定時才可設定 trunc 模式。
  4. 只要 trunc 沒被設定,就可以設定 app 模式。在 app 模式下,即使沒有顯式指定 out 模式,文件也總是以輸出方式被打開。
  5. 默認情況下,以 out 模式打開的文件同時使用 trunc 模式,即會被截斷(內容被丟棄)。
    • 為了保留以 out 模式打開的文件的內容,我們必須同時指定 app 模式,這樣只會將數據追加寫到文件末尾;
    • 或者同時指定 in 模式,即打開文件同時進行讀寫操作。
  6. atebinary 模式可用于任何類型的文件流對象,且可以與其他任何文件模式組合使用。

關于第五點,舉例詳細講一下:

/*截斷*/
ofstream out1("file1"); // 隱含以out模式打開文件并截斷文件
ofstream out2("file1", ofstream::out); // 隱含地截斷文件
ofstream out3("file1", ofstream::out | ofstream::trunc); 
// 顯式實現out模式打開文件并截斷/*app模式保留文件內容*/
ofstream app1("file2", ofstream:app); // 隱含out模式
ofstream app2("file2", ofstream:out | ofstream:app); 

文件光標函數

tellg() / tellp()

該函數沒有參數,返回 pos_type 類型的值,就是一個整數,代表當前 讀取光標【tellg】 / 寫入光標【tellp】 的位置距文件首的字節數。


seekg() / seekp()

  • g 表示 get,指示函數在輸入流上工作。該函數的作用移動讀操作光標。
  • pput縮寫,指示函數在輸出流上工作。seekp用于移動寫操作光標。
// 一個參數
basic_istream<Elem, Tr>& seekg(  pos_type pos
);// 兩個參數
basic_istream<Elem, Tr>& seekg(  off_type off,                                     ios_base::seekdir way
);// seekp 函數原型及參數信息同 seekg
  • pos:移動讀取指針的絕對位置。要求傳入的參數類型與函數tellg(見下文)的返回值類型相同。
  • off:偏移量,單位字節(B)。正數表示向右偏移,負數表示向左偏移。
  • way:基地址,off根據該地址進行偏移。有下面三個取值:
描述模式標志
文件首std::ios::beg
文件尾std::ios::end
當前光標位置std::ios::cur

注意,如果目前已經在文件末尾,則在調用seekg之前,必須清除文件末尾的標志:

fstream ioFile("文件路徑");ioFile.get(ch); // 先將字符讀入流
while (!ioFile.fail())
{cout.put(ch); // 再將流中內容輸出到屏幕ioFile.get(ch); // 
}
// 假設此時已經讀取到文件流對象的末尾
/* 缺少調用 clear() */
文件流對象.seekg(0L, ios::beg); // 移動到文件開頭
文件流對象.tellg(); // 返回-1,說明上一步并為真正移動到文件開頭// 正確做法
文件流對象.clear();
文件流對象.seekg(0L, ios::beg); // 移動到文件開頭

向文件存儲內容/讀取文件內容

// url 文件路徑
void write(std::string &url){std::ofstream fwrite(url, std::ios::binary);if (!fwrite.is_open()) {std::cout << "open url fail" << std::endl;}/* 寫入數據 */// 內置類型int i = 1024;fwrite.write((const char *) &i, sizeof(i));// 數組std::vector<std::string> stringVec = {"a", "b", "c"};int64_t vecTotalSize = sizeof(std::string) * stringVec.size();fwrite.write((const char *) stringVec.data(), vecTotalSize);// 寫入數組數據時需要記錄總數據的長度fwrite.write((const char *) &vecTotalSize, sizeof(vecTotalSize));// 自定義結構People people;people.name = "lihua";people.age = 21;fwrite.write((const char *) &people, sizeof(people));// 關閉流fwrite.close();
}
void read(std::string &url){std::ifstream fread;fread.open(url, std::ofstream::binary);if (!fread.is_open()) {std::cout << "open url fail" << std::endl;}/* 讀取數據,順序和寫入順序相反 */// 自定義結構People people;int peopleLen = sizeof(people);fread.seekg(-peopleLen, std::ios::end);fread.read((char *) &people, peopleLen);// 數組int64_t vecSize; // 先讀取數組數據的長度const int vecSizeLen = sizeof(vecSize);// std::ios::cur 在 People 數據的結尾處// 讀取 vecSize 需要將指針左移到 vecSize 數據的開頭// 這就需要經過 peopleLen 和 vecSizeLen 兩個長度fread.seekg(-peopleLen - vecSizeLen, std::ios::cur);fread.read((char *) &vecSize, vecSizeLen); //8 bytesint64_t stringVecSize = vecSize / sizeof(std::string); // 根據數組數據的長度算出數組的大小std::vector<std::string> stringVec;stringVec.resize(stringVecSize);fread.seekg(-vecSizeLen - vecSize, std::ios::cur);auto pos = fread.tellg();fread.read((char *) stringVec.data(), vecSize);pos = fread.tellg();// 內置類型int j;int len = sizeof(j);fread.seekg(-vecSize - len, std::ios::cur);fread.read((char*) &j, len);// 關閉流fread.close();
}

string流

同樣的,除了繼承自 iostream 的操作,sstream 也增加了獨有的操作。
在這里插入圖片描述

istringstream

我們經常會碰到處理整行字符串的問題,比如:比較版本號

用雙指針截取字符串當然是一種方法,但是使用 istringstream 這個標準庫提供的利器會更加方便。當然,兩種方法的時間、空間復雜度是一樣的。

下面通過分析 istringstream 的使用來進一步理解如何用:

class Solution {
public:int compareVersion(string version1, string version2) {istringstream in1(version1); // 將文本version1與輸入流in1綁定istringstream in2(version2);int a, b;char c;while(in1.good() || in2.good()){in1 >> a; // 從in1中讀取int數據到a中,遇到空白符or非int數據停下in2 >> b;if(a > b) return 1;if(a < b) return -1;a = b = 0;in1 >> c; // 從in1中讀取char類型數據到c中,遇到空白符or非char數據停下in2 >> c;}return 0;}
};

再比如,有這樣的輸入,人名和他們的常用密碼,一個人可能有多種常用密碼:

cmy 12345 22345
lx 6644
lhy 6633 1221 5665

那么我們可以這樣處理:

struct per_pw{string name;vector<string> pw;
}
string s, word; // s暫存來自輸入的一行文本
vector<per_pw> people;
while(getline(cin, s)){ // 處理一行文本,也就是一個人的信息per_pw pp;istringstream in(s); // 將in綁定到剛讀取的sin >> pp.name; // 讀取名字while(in >> word) // 讀取密碼pp.pw.push_back(word); // 密碼存入pp的pw數組中people.push_back(pp); // 將這個人的信息保存在people數組中
}

ostringstream

當我們希望將多個輸出最后一起打印時,ostringstream 是很有用的。舉個簡單的例子:

ostringstream out; // 創建一個未綁定的輸出流
vector<string> vs = {"cmy", "lx", "lhy"};
for (string s : vs) {out << s << " ";
}
cout << out.str() << endl; 
// str():返回out保存的string的拷貝,也就是將out轉換為string類型。

我們使用標準的輸出運算符<<out 寫入數據,有趣的是,這些寫入操作實際上轉換為 string 操作,向 out 中的 string 對象添加字符。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/443742.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/443742.shtml
英文地址,請注明出處:http://en.pswp.cn/news/443742.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Hibernate 更新部分更改的字段 hibernate update

Hibernate 中如果直接使用 Session.update(Object o);或則是Session.updateOrUpdate(Object o); 會把這個表中的所有字段更新一遍。 如&#xff1a; ExperClass4k e new ExperClass4k(); e.setTime(time); e.setQ_num(q_num); e.setK(k); if (str "finch_fix")…

C++ 類的行為 | 行為像值的類、行為像指針的類、swap函數處理自賦值

文章目錄概念行為像值的類行為像指針的類概念引用計數動態內存實現計數器類的swap概念swap實現自賦值概念 行為像值的類和行為像指針的類這兩種說法其實蠻拗口的&#xff0c;這也算是 《CPrimer》 翻譯的缺點之一吧。。。 其實兩者的意思分別是&#xff1a; 行為像值的類&am…

C++ 右值引用 | 左值、右值、move、移動語義、引用限定符

文章目錄C11為什么引入右值&#xff1f;區分左值引用、右值引用move移動語義移動構造函數移動賦值運算符合成的移動操作小結引用限定符規定this是左值or右值引用限定符與重載C11為什么引入右值&#xff1f; C11引入了一個擴展內存的方法——移動而非拷貝&#xff0c;移動較之拷…

且談關于最近軟件測試的面試

前段時間有新的產品需要招人&#xff0c;安排和參加了好幾次面試&#xff0c;下面就談談具體的面試問題&#xff0c;在面試他人的同時也面試自己。 面試問題是參與面試同事各自設計的&#xff0c;我也不清楚其他同事的題目&#xff0c;就談談自己設計的其中2道題。 過去面試總是…

C++ 多態 | 虛函數、抽象類、虛函數表

文章目錄多態虛函數重寫重定義&#xff08;參數不同&#xff09;協變&#xff08;返回值不同&#xff09;析構函數重寫&#xff08;函數名不同&#xff09;final和override重載、重寫、重定義抽象類多態的原理虛函數常見問題解析虛函數表多態 一種事物&#xff0c;多種形態。換…

C++ 運算符重載(一) | 輸入/輸出,相等/不等,復合賦值,下標,自增/自減,成員訪問運算符

文章目錄輸出運算符<<輸入運算符>>相等/不等運算符復合賦值運算符下標運算符自增/自減運算符成員訪問運算符輸出運算符<< 通常情況下&#xff0c;輸出運算符的第一個形參是一個 非常量ostream對象的引用 。之所以 ostream 是非常量是因為向流寫入內容會改變…

C++ 重載函數調用運算符 | 再探lambda,函數對象,可調用對象

文章目錄重載函數調用運算符lambdalambda等價于函數對象lambda等價于類標準庫函數對象可調用對象與function可調用對象function函數重載與function重載函數調用運算符 函數調用運算符必須是成員函數。 一個類可以定義多個不同版本的調用運算符&#xff0c;互相之間應該在參數數…

C++ 運算符重載(二) | 類型轉換運算符,二義性問題

文章目錄類型轉換運算符概念避免過度使用類型轉換函數解決上述問題的方法轉換為 bool顯式的類型轉換運算符類型轉換二義性重載函數與類型轉換結合導致的二義性重載運算符與類型轉換結合導致的二義性類型轉換運算符 概念 類型轉換運算符&#xff08;conversion operator&#…

Tomcat中JVM內存溢出及合理配置

Tomcat本身不能直接在計算機上運行&#xff0c;需要依賴于硬件基礎之上的操作系統和一個Java虛擬機。Tomcat的內存溢出本質就是JVM內存溢出&#xff0c;所以在本文開始時&#xff0c;應該先對Java JVM有關內存方面的知識進行詳細介紹。 一、Java JVM內存介紹 JVM管理兩種類型的…

俄羅斯農民乘法 | 快速乘

文章目錄概念概念 俄羅斯農民乘法經常被用于兩數相乘取模的場景&#xff0c;如果兩數相乘已經超過數據范圍&#xff0c;但取模后不會超過&#xff0c;我們就可以利用這個方法來拆位取模計算貢獻&#xff0c;保證每次運算都在數據范圍內。 A 和 B 兩數相乘的時候我們如何利用加…

Linux網絡編程 | socket選項設定 及 網絡信息API

文章目錄讀取和設置 socket 選項SO_REUSEADDRSO_RCVBUF 和 SO_SNDBUFSO_RCVLOWAT 和 SO_SNDLOWATSO_LINGER 選項網絡信息APIgethostbyname 和 gethostbyaddrgetservbyname 和 getservbyportgetaddrinfogetnameinfo讀取和設置 socket 選項 正如 fcntl 系統調用是控制文件描述符…

Linux | 高級I/O函數

文章目錄創建文件描述符的函數pipe函數dup函數、dup2函數讀取或寫入數據readv函數、writev函數零拷貝sendfile函數splice函數tee函數進程間通信——共享內存mmap函數 和 munmap函數控制文件描述符fcntl函數創建文件描述符的函數 pipe函數 不再贅述&#xff0c;詳情見我的另一…

分布式理論:CAP、BASE | 分布式存儲與一致性哈希

文章目錄分布式理論CAP定理BASE理論分布式存儲與一致性哈希簡單哈希一致性哈希虛擬節點分布式理論 CAP定理 一致性&#xff08;Consistency&#xff09;&#xff1a; 在分布式系統中的所有數據副本&#xff0c;在同一時刻是否一致&#xff08;所有節點訪問同一份最新的數據副…

Tomcat服務器性能優化

一、概述 本文檔主要介紹了Tomcat的性能調優的原理和方法。可作為公司技術人員為客戶Tomcat系統調優的技術指南&#xff0c;也可以提供給客戶的技術人員作為他們性能調優的指導手冊。 二、調優分類 由于Tomcat的運行依賴于JVM&#xff0c;從虛擬機的角度我們把Tomcat的調整分為…

分布式系統概念 | 分布式事務:2PC、3PC、本地消息表

文章目錄分布式事務2PC&#xff08;二階段提交協議&#xff09;執行流程優缺點3PC&#xff08;三階段提交協議&#xff09;執行流程優缺點本地消息表&#xff08;異步確保&#xff09;分布式事務 分布式事務就是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分…

數據結構算法 | 單調棧

文章目錄算法概述題目下一個更大的元素 I思路代碼下一個更大元素 II思路代碼132 模式思路代碼接雨水思路算法概述 當題目出現 「找到最近一個比其大的元素」 的字眼時&#xff0c;自然會想到 「單調棧」 。——三葉姐 單調棧以嚴格遞增or遞減的規則將無序的數列進行選擇性排序…

最長下降子序列

文章目錄題目解法DP暴搜思路代碼實現貪心二分思路代碼實現題目 給出一組數據 nums&#xff0c;求出其最長下降子序列&#xff08;子序列允許不連續&#xff09;的長度。&#xff08;類似于lc的最長遞增子序列&#xff09; 示例&#xff1a; 輸入&#xff1a; 6 // 數組元素個…

Linux 服務器程序規范、服務器日志、用戶、進程間的關系

文章目錄服務器程序規范日志rsyslogd 守護進程syslog函數openlog函數setlogmask函數closelog函數用戶進程間的關系進程組會話系統資源限制改變工作目錄和根目錄服務器程序后臺化服務器程序規范 Linux 服務器程序一般以后臺進程&#xff08;守護進程[daemon]&#xff09;形式運…

IO模型 :阻塞IO、非阻塞IO、信號驅動IO、異步IO、多路復用IO

文章目錄IO模型阻塞IO非阻塞IO信號驅動IO多路復用IO異步IOIO模型 根據各自的特性不同&#xff0c;IO模型被分為阻塞IO、非阻塞IO、信號驅動IO、異步IO、多路復用IO五類。 最主要的兩個區別就是阻塞與非阻塞&#xff0c;同步與異步。 阻塞與非阻塞 阻塞與非阻塞最主要的區別就…

Tomcat服務器集群與負載均衡實現

一、前言 在單一的服務器上執行WEB應用程序有一些重大的問題&#xff0c;當網站成功建成并開始接受大量請求時&#xff0c;單一服務器終究無法滿足需要處理的負荷量&#xff0c;所以就有點顯得有點力不從心了。另外一個常見的問題是會產生單點故障&#xff0c;如果該服務器壞掉…