基本文件I / O
C ++中的文件I / O與普通I / O非常相似(有一些小的複雜性)。C ++中有3個基本文件I / O類:ifstream(派生自istream),ofstream(派生自ostream)和fstream(派生自iostream)。這些類分別進行文件輸入,輸出和輸入/輸出。要使用文件I / O類,您需要包含fstream標頭。
與已經可以使用的cout,cin,cerr和clog流不同,文件流必須由程序員明確設置。但是,這非常簡單:打開一個文件進行讀取和/或寫入,只需實例化相應文件I / O類的對象,並將文件名作爲參數。然後使用插入(<<)或提取(>>)運算符來寫入或讀取文件中的數據。完成後,有幾種方法可以關閉文件:顯式調用close()函數,或者讓文件I / O變量超出範圍(文件I / O類析構函數將爲您關閉文件) 。
文件輸出
要在以下示例中執行文件輸出,我們將使用ofstream類。這非常簡單:
#include <fstream>
#include <iostream>
#include <cstdlib> //exit()
int main()
{
using namespace std;
// ofstream用於寫入文件
// 我們將創建一個名爲Sample.dat的文件
ofstream outf("Sample.dat");
//如果我們無法打開輸出文件流進行寫入
if (!outf)
{
// 打印錯誤並且退出
cerr << "Uh oh, Sample.dat could not be opened for writing!" << endl;
exit(1);
}
// 我們將在這個文件中寫入兩行
outf << "This is line 1" << endl;
outf << "This is line 2" << endl;
return 0;
// 當超出範圍
// 這個ofstream析構函數將關閉這個文件
}
如果查看項目目錄,應該會看到一個名爲Sample.dat的文件。如果用文本編輯器打開它,你會發現它確實包含我們寫入文件的兩行。
請注意,也可以使用put()函數將單個字符寫入文件。
文件輸入
現在,我們將獲取我們在上一個示例中編寫的文件,並從磁盤中讀回來。請注意,如果我們已到達文件末尾(EOF),ifstream將返回0。我們將使用這個事實來確定要閱讀多少。
#include <fstream>
#include <iostream>
#include <string>
#include <cstdlib> // exit()
int main()
{
using namespace std;
// ifstream用於讀取文件
// 我們將從一個名爲Sample.dat的文件中讀取
ifstream inf("Sample.dat");
//如果我們無法打開輸出文件流進行讀取
if (!inf)
{
// 打印錯誤並且退出
cerr << "Uh oh, Sample.dat could not be opened for reading!" << endl;
exit(1);
}
// 雖然還有東西可以read
while (inf)
{
// 將文件中的內容讀入字符串並打印出來
std::string strInput;
inf >> strInput;
cout << strInput << endl;
}
return 0;
// 當超出範圍
// 這個ofstream析構函數將關閉這個文件
}
這會產生結果:
This
is
line
1
This
is
line
2
嗯,那不是我們想要的。請記住,提取運算符處理“格式化輸出”,並在空白處打破。爲了讀取整行,我們必須使用getline()函數。
#include <fstream>
#include <iostream>
#include <string>
#include <cstdlib> // exit()
int main()
{
using namespace std;
// ifstream用於讀取文件
// 我們將從一個名爲Sample.dat的文件中讀取
ifstream inf("Sample.dat");
// 如果我們無法打開輸入文件流進行讀取
if (!inf)
{
// 打印錯誤並且退出
cerr << "Uh oh, Sample.dat could not be opened for reading!" << endl;
exit(1);
}
// 雖然還有一些東西需要閱讀
while (inf)
{
// 將文件中的內容讀入字符串並打印出來
std::string strInput;
getline(inf, strInput);
cout << strInput << endl;
}
return 0;
// 當超出範圍
// 這個ofstream析構函數將關閉這個文件
}
這會產生結果:
This is line 1
This is line 2
緩衝輸出
可以緩衝C ++中的輸出。這意味着輸出到文件流的任何內容都可能無法立即寫入磁盤。相反,可以對幾個輸出操作進行批處理和一起處理。這主要是出於性能原因。將緩衝區寫入磁盤時,這稱爲刷新緩衝區。導致緩衝區刷新的一種方法是關閉文件,緩衝區的內容將刷新到磁盤,然後文件將被關閉。
緩衝通常不是問題,但在某些情況下,它可能導致不警惕的併發症。在這種情況下,主要的罪魁禍首是緩衝區中有數據,然後程序立即終止(通過崩潰或通過調用exit())。在這些情況下,不執行文件流類的析構函數,這意味着文件永遠不會關閉,這意味着永遠不會刷新緩衝區。在這種情況下,緩衝區中的數據不會寫入磁盤,並且會永久丟失。這就是爲什麼在調用exit()之前顯式關閉任何打開的文件總是一個好主意。
可以使用ostream :: flush()函數手動刷新緩衝區或將std :: flush發送到輸出流。這些方法中的任何一個都可以用於確保緩衝區的內容立即寫入磁盤,以防程序崩潰。
一個有趣的注意事項是std :: endl; 還會刷新輸出流。因此,過度使用std :: endl(導致不必要的緩衝區刷新)會在執行緩衝I / O時產生性能影響,其中刷新是昂貴的(例如寫入文件)。出於這個原因,具有性能意識的程序員通常會使用’\ n’而不是std :: endl在輸出流中插入換行符,以避免不必要的緩衝區刷新。
文件模式
如果我們嘗試寫入已存在的文件會發生什麼?再次運行輸出示例顯示每次運行程序時都會完全覆蓋原始文件。相反,如果我們想在文件的末尾添加更多數據呢?事實證明,文件流構造函數採用可選的第二個參數,允許您指定有關如何打開文件的信息。此參數稱爲模式,它接受的有效標誌位於Ios類中。
Ios文件模式 | 含義 |
---|---|
app | 以附加模式打開文件 |
ate | 在讀/寫之前尋找文件的末尾 |
binary | 以二進制模式打開文件(而不是文本模式) |
in | 以讀取模式打開文件(ifstream的默認值) |
out | 以寫入模式打開文件(ofstream的默認值) |
trunc | 如果文件已存在,則刪除該文件 |
可以通過將它們按位OR運算來指定多個標誌(使用|運算符)。Ifstream默認爲ios :: in file模式。Ofstream默認爲ios :: out文件模式。並且fstream默認爲ios :: in | ios :: out文件模式,意味着您可以默認讀取和寫入。
讓我們編寫一個程序,將另外兩行添加到我們之前創建的Sample.dat文件中:
#include <cstdlib> // for exit()
#include <iostream>
#include <fstream>
int main()
{
using namespace std;
// 我們將通過ios:app標誌告訴ofstream追加
// 而不是重寫文件。我們不需要傳入ios :: out
// 因爲ofstream默認爲ios :: out
ofstream outf("Sample.dat", ios::app);
// 如果我們無法打開輸出文件流進行寫入
if (!outf)
{
// 打印錯誤並退出
cerr << "Uh oh, Sample.dat could not be opened for writing!" << endl;
exit(1);
}
outf << "This is line 3" << endl;
outf << "This is line 4" << endl;
return 0;
// 當超出範圍
// 這個ofstream析構函數將關閉這個文件
}
現在,如果我們看看Sample.dat(使用上面打印其內容的示例程序之一,或者在文本編輯器中加載它),我們將看到以下內容:
This is line 1
This is line 2
This is line 3
This is line 4
使用open()顯式打開文件
就像可以使用close()顯式關閉文件流一樣,也可以使用open()顯式打開文件流。open()的工作方式與文件流構造函數一樣,它採用文件名和可選的文件模式。
例如:
ofstream outf("Sample.dat");
outf << "This is line 1" << endl;
outf << "This is line 2" << endl;
outf.close(); // 顯式關閉文件
// 哎呀,我們忘記了什麼
outf.open("Sample.dat", ios::app);
outf << "This is line 3" << endl;
outf.close();