下面介紹C++中的iostream、fstream、sstream的使用方法,內容出自C++primer第八章內容。
一、 IO類
1.三種IO類型
加w的類型爲對寬字符wchar_t的操作,如wistream、wostream中的wcin、wcot、werror。
ifstream和isteingstream都繼承自istream,可使用cin的功能(如>>和getline等)。ofstream和ostringstream都繼承自ostream,可使用cout的功能
2.不能拷貝或對IO對象賦值:
不能將形參後返回值設置爲流類型,通常以引用方式傳遞和返回流進行IO操作,並且返回的引用不能爲const,因爲讀寫IO會改變其狀態。
3.條件狀態
IO類定義的函數和標誌,便於訪問和控制流條件狀態。
IO流讀寫錯誤時(比如期望輸入int,結果輸入了字符或文件結束標誌),後續IO操作都會失敗。爲保證流對象的狀態正確,一般將它當作條件使用,如果輸入成功,流保持有效狀態,條件爲真。
while(cin>>word)
可通過上面表中的函數查詢標誌位狀態,如操作good在所有錯誤位未置位情況下返回true,而bad\fail\eof則在對應錯誤位置置位時返回true。
無參數clear可以清除所有錯誤標誌位,執行後調用good會返回true。帶參數clear接收iostate值,表示流的新狀態,可以通過該功能復位指定狀態位。
3.管理輸出緩衝
每個輸出流都管理一個緩衝區用來保存程序讀寫的數據,系統可將多個輸出組合爲單一的設備寫操作以提升性能。導致緩衝刷新(數據真正寫入設備或文件)包括:程序正常結束、緩衝區滿、endl、unitbuf、一個輸出流可能被關聯到另一個流。如果程序異常終止,輸出緩衝區不會被刷新。
二、 文件輸入輸出
- 文件讀寫
Ifstream從文件中讀取數據,ofstream向文件寫入數據,fstream讀寫給定文件。這些類型繼承了iostream類型的<< 、>>、getline等行爲,還增加了下表中的操作:
通過定義文件流對象可將對象和文件關聯起來,通過open成員函數定位給定文件並打開爲讀或寫模式,也可在創建文件流對象提供文件名,然後open會被自動調用。
ofstream out1;
out1.open("filename.txt", ios::in | ios::binary);//打開文件方法1
//ofstream out("filename.txt");//打開文件方法2 在構造函數直接調用了open函數
通常需要對open是否成功進行檢測:if (out1.is_open())。
在要求使用基類對象的地方可使用繼承類型對象替代,iostream類型引用或指針的參數可用fstream後sstream類型調用。
- 文件模式
打開文件方式如下:
指定文件模式限制:
Ifstream關聯文件默認in模式,ofstream關聯文件默認out模式,fstream關聯文件默認in和out模式。
- 以out模式打開文件會丟棄已有數據(很重要)
默認情況ofstream打開文件,內容會被截斷清空,阻止該情況發生需要指定app模式。
(刪除文件:將文件的目錄項刪掉並釋放磁盤塊,截斷文件:將文件中的部位數據刪掉 不刪除目錄項 )
三、 String流
istringstream從string中讀取數據,ostringstream向string中寫入數據,頭文件stringstream既可讀又可寫string數據。Sstream也繼承了iostrea,還增加了下面的操作:
- istringstream
當我們工作是對整行文本或行內單個單詞進行處理時,通常可以考慮istringstream。如有下面的數據,姓名後跟着若干號碼:
anmy 2345 56437
daga 23456 67543
hsagg 26789 8765
定義簡單地類描述輸入數據:
struct PersonInfo{
string name;
vector<string> phones;
};
程序會讀取數據文件並創建PersonInfo的vector,,vector中每個元素對應文件中的一條記錄,在循環中處理輸入輸出,每個循環讀取一條記錄。如下所示。
string line, word;
vector<PersonInfo> people;
while (getline(cin,line)){
PersonInfo info;
istringstream record(line);
record >> info.name;
while (record >> word){
info.phones.push_back(word);
}
people.push_back(info);
}
通過getline從標準輸入讀取整條記錄,如果調用成功,line中將保存着從輸入文件而來的一條記錄。在循環中定義了局部PersonInfo對象保存當前記錄,接下來將istringstream和剛剛讀取的文本綁定,然後通過運算符記錄當前中的每個元素。
2. ostringstream
當逐步構造輸出,希望最後一起打印時,採用ostringstream很有用。比如對上面的例子只輸出有效的電話號碼。對無效的號碼不會將它們輸出到新文件中,而是打印一條包含人名和無效號碼的錯誤信息。
for (const auto &entry : people) {
ostringstream formatted, badNums;
for (const auto &nums : entry.phones){
if (strcmp(nums.c_str(), "8766") < 0)
badNums << "bad " << nums;
else {
formatted << "ok " << nums;
}
}
if (badNums.str().empty())
cout << entry.name << " " << formatted.str() << endl;
else
cerr << "input error:" << entry.name << "invalid number " << badNums.str() << endl;
}
#include<iostream>
#include<fstream>
#include<sstream>
#include<vector>
using namespace std;
const char * filename = "test.txt";
struct PersonInfo{
string name;
vector<string> phones;
};
int main() {
char buffer[1024];
/***************************文本文件讀寫*******************************/
ofstream out1;
out1.open("test.txt");//ofstream默認情況或不指定app模式下打開文件會導致文件內容丟失
out1.open(filename, ios::out | ios::app);//打開文件方法1,指定app模式
ofstream out1("test.txt", ios::out | ios::app);//打開文件方法2 在構造函數直接調用了open函數
if (out1.is_open()) {
out1 << "hi!\n";
out1.close();
}
else
cout << "error opening file";
ifstream in(filename, ios::in);
if (!in.is_open()) {
cout << "error opening file"; exit(1);
}
while (!in.eof()) {
in.getline(buffer,20);//getline 第二個參數不能比實際行數小
cout << buffer << endl;
}
//in.close();////當指針直到eof後,若要重置指針位置到開頭,需要採用下面兩種方法
//in.open("test.txt", ios::in);//方法1:要重新打開文件後才能將指針重定位到開始位置
in.clear(); //方法2:對流狀態標誌進行清除
long length1, length2;
in.seekg(0, ios::beg);
length1 = in.tellg();
in.seekg(0,ios::end);//將get指針移動到beg文件結束位置
length2 = in.tellg();
in.close();
cout << "len1:" << length1 << endl;
cout << "len2:" << length2 << endl;
cout << "len:" << length2 - length1 << " bytes" << endl;
/***************************二進制文件讀寫*******************************/
fstream binInOut(filename,ios::in|ios::ate|ios::binary);
char * buffer1;
long size = binInOut.tellg();
binInOut.clear();
binInOut.seekg(0,ios::beg);
buffer1 = new char[size];
binInOut.read(buffer1, size);
cout <<"size:"<<size <<" buffer1:" << buffer1 << endl;//不知道爲什麼末尾會多出亂碼
//cout << "buffer1:" << static_cast<const void*>(buffer1) << endl;
binInOut.close();
delete[] buffer1;
/***************************字符串讀寫*******************************/
/* 輸入字符串數據
anmy 2345 56437
daga 23456 67543
hsagg 26789 8765
輸入完成後 回車+ctrl + c
*/
string line, word;
vector<PersonInfo> people;
while (getline(cin,line)){
PersonInfo info;
istringstream record(line);
record >> info.name;
while (record >> word){
info.phones.push_back(word);
}
people.push_back(info);
}
ofstream out1("test.txt", ios::out | ios::app);//ofstream 寫入數據
if (out1.is_open()) {
for (int i = 0; i < people.size(); i++){
for (int j = 0; j < people.at(i).phones.size(); j++){
out1 << people.at(i).name << ends << people.at(i).phones.at(j) << endl;
cout << people.at(i).name << ends << people.at(i).phones.at(j) << endl;
}
}
out1.close();
}
for (const auto &entry : people) {
ostringstream formatted, badNums;
for (const auto &nums : entry.phones){
if (strcmp(nums.c_str(), "8766") < 0)
badNums << "bad " << nums;
else {
formatted << "ok " << nums;
}
}
if (badNums.str().empty())
cout << entry.name << " " << formatted.str() << endl;
else
cerr << "input error:" << entry.name << "invalid number " << badNums.str() << endl;
}
return 0;
}