讀檔案直到最後

逐行讀取文字檔案

ifstream 文件中通常不清楚逐行讀取文字檔案直到結尾的正確方法。讓我們考慮初學者 C++程式設計師所做的一些常見錯誤,以及讀取檔案的正確方法。

沒有空格字元的行

為簡單起見,我們假設檔案中的每一行都不包含空格符號。

ifstreamoperator 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 個以上整數的檔案,它將嘗試在陣列外寫入並執行到未定義的行為。