讀檔案直到最後
逐行讀取文字檔案
從 ifstream
文件中通常不清楚逐行讀取文字檔案直到結尾的正確方法。讓我們考慮初學者 C++程式設計師所做的一些常見錯誤,以及讀取檔案的正確方法。
沒有空格字元的行
為簡單起見,我們假設檔案中的每一行都不包含空格符號。
ifstream
有 operator bool()
,當流沒有錯誤並準備好讀取時返回 true。此外,ifstream::operator >>
返回對流本身的引用,因此我們可以使用非常優雅的語法在一行中讀取和檢查 EOF(以及錯誤):
std::ifstream ifs("1.txt");
std::string s;
while(ifs >> s) {
std::cout << s << std::endl;
}
帶有空格字元的行
ifstream::operator >>
讀取流直到出現任何空格字元,因此上面的程式碼將在單獨的行上列印一行中的單詞。要閱讀所有內容直到行尾,請使用 std::getline
而不是 ifstream::operator >>
。getline
返回對其使用的執行緒的引用,因此可以使用相同的語法:
while(std::getline(ifs, s)) {
std::cout << s << std::endl;
}
顯然,std::getline
也應該用於讀取單行檔案直到結束。
一次將檔案讀入緩衝區
最後,讓我們從開頭到結尾讀取檔案而不停止任何字元,包括空格和換行符。如果我們知道確切的檔案大小或長度的上限是可以接受的,我們可以調整字串的大小,然後閱讀:
s.resize(100);
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
s.begin());
否則,我們需要將每個字元插入字串的末尾,因此 std::back_inserter
是我們需要的:
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
std::back_inserter(s));
或者,可以使用帶有迭代器範圍引數的建構函式初始化具有流資料的集合:
std::vector v(std::istreambuf_iterator<char>(ifs),
std::istreambuf_iterator<char>());
請注意,如果將 ifs
作為二進位制檔案開啟,這些示例也適用:
std::ifstream ifs("1.txt", std::ios::binary);
複製流
可以使用流和迭代器將檔案複製到另一個檔案:
std::ofstream ofs("out.file");
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
std::ostream_iterator<char>(ofs));
ofs.close();
或使用相容介面重定向到任何其他型別的流。例如 Boost.Asio 網路流:
boost::asio::ip::tcp::iostream stream;
stream.connect("example.com", "http");
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
std::ostream_iterator<char>(stream));
stream.close();
陣列
由於迭代器可能被認為是指標的泛化,上面示例中的 STL 容器可能會被原生陣列替換。以下是如何將數字解析為陣列:
int arr[100];
std::copy(std::istream_iterator<char>(ifs), std::istream_iterator<char>(), arr);
請注意緩衝區溢位,因為陣列在分配後無法即時調整大小。例如,如果上面的程式碼將包含一個包含 100 個以上整數的檔案,它將嘗試在陣列外寫入並執行到未定義的行為。