一、基本文件 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 类析构函数将为您关闭文件) .
1、File output
为了在以下示例中进行文件输出,我们将使用 ofstream 类。
#include <fstream>
#include <iostream>
int main()
{
// ofstream is used for writing files
// We'll make a file called Sample.txt
std::ofstream outf{ "Sample.txt" };
// If we couldn't open the output file stream for writing
if (!outf)
{
// Print an error and exit
std::cerr << "Uh oh, Sample.txt could not be opened for writing!\n";
return 1;
}
// We'll write two lines into this file
outf << "This is line 1" << '\n';
outf << "This is line 2" << '\n';
return 0;
// When outf goes out of scope, the ofstream
// destructor will close the file
}
如果您查看您的项目目录,您应该会看到一个名为 Sample.txt 的文件。 如果你用文本编辑器打开它,你会看到它确实包含我们写入文件的两行。
请注意,也可以使用 put() 函数将单个字符写入文件。
2、File input
现在,我们将获取我们在上一个示例中编写的文件并从磁盘读回它。 请注意,如果我们已到达文件末尾 (EOF),ifstream 将返回 0。 我们将使用这个事实来确定要阅读多少。
#include <fstream>
#include <iostream>
#include <string>
int main()
{
// ifstream is used for reading files
// We'll read from a file called Sample.txt
std::ifstream inf{ "Sample.txt" };
// If we couldn't open the output file stream for reading
if (!inf)
{
// Print an error and exit
std::cerr << "Uh oh, Sample.txtcould not be opened for reading!\n";
return 1;
}
// While there's still stuff left to read
while (inf)
{
// read stuff from the file into a string and print it
std::string strInput;
inf >> strInput;
std::cout << strInput << '\n';
}
return 0;
// When inf goes out of scope, the ifstream
// destructor will close the file
}
输出如下
This
is
line
1
This
is
line
2
这不是我们想要的。 请记住,提取运算符会在空格处中断。为了读取整行,我们必须使用 getline() 函数。
#include <fstream>
#include <iostream>
#include <string>
int main()
{
// ifstream is used for reading files
// We'll read from a file called Sample.txt
std::ifstream inf{ "Sample.txt" };
// If we couldn't open the input file stream for reading
if (!inf)
{
// Print an error and exit
std::cerr << "Uh oh, Sample.txt could not be opened for reading!\n";
return 1;
}
// While there's still stuff left to read
while (inf)
{
// read stuff from the file into a string and print it
std::string strInput;
std::getline(inf, strInput);
std::cout << strInput << '\n';
}
return 0;
// When inf goes out of scope, the ifstream
// destructor will close the file
}
输出如下
This is line 1
This is line 2
3、Buffered output
C++ 中的输出可能会被缓冲。 这意味着输出到文件流的任何内容都可能不会立即写入磁盘。 相反,可以将多个输出操作一起批处理和处理。 这样做主要是出于性能原因。 当缓冲区写入磁盘时,这称为刷新缓冲区。 导致缓冲区被刷新的一种方法是关闭文件——缓冲区的内容将被刷新到磁盘,然后文件将被关闭。
缓冲通常不是问题,但在某些情况下,它可能会给粗心的人带来麻烦。 在这种情况下,罪魁祸首是缓冲区中有数据,然后程序立即终止(通过崩溃或调用 exit())。 在这些情况下,文件流类的析构函数不会被执行,这意味着文件永远不会关闭,这意味着缓冲区永远不会被刷新。 在这种情况下,缓冲区中的数据不会写入磁盘,并且会永远丢失。 这就是为什么在调用 exit() 之前显式关闭所有打开的文件总是一个好主意的原因。
可以使用 ostream::flush() 函数手动刷新缓冲区或将 std::flush 发送到输出流。 这些方法中的任何一种都有助于确保缓冲区的内容立即写入磁盘,以防程序崩溃。
一个有趣的注意事项是 std::endl; 还刷新输出流。 因此,过度使用 std::endl(导致不必要的缓冲区刷新)可能会在执行刷新代价高昂的缓冲 I/O(例如写入文件)时对性能产生影响。 出于这个原因,注重性能的程序员通常会使用“\n”而不是 std::endl 在输出流中插入换行符,以避免不必要的缓冲区刷新。
4、File modes
如果我们尝试写入一个已经存在的文件会发生什么? 再次运行输出示例显示每次运行程序时都完全覆盖了原始文件。 相反,如果我们想在文件末尾附加更多数据怎么办? 事实证明,文件流构造函数采用可选的第二个参数,允许您指定有关如何打开文件的信息。 此参数称为模式,它接受的有效标志位于 Ios 类中。
可以通过按位或将它们一起指定多个标志(使用 | 运算符)。 Ifstream 默认为 std::ios::in 文件模式。 Ofstream 默认为 std::ios::out 文件模式。 fstream 默认为 std::ios::in | std::ios::out 文件模式,这意味着默认情况下您可以读写。
由于 fstream 的设计方式,如果使用 std::ios::in 并且打开的文件不存在,它可能会失败。 如果您需要使用 fstream 创建新文件,请仅使用 std::ios::out 模式。
让我们编写一个程序,在我们之前创建的 Sample.txt 文件中再添加两行:
#include <iostream>
#include <fstream>
int main()
{
// We'll pass the ios:app flag to tell the ofstream to append
// rather than rewrite the file. We do not need to pass in std::ios::out
// because ofstream defaults to std::ios::out
std::ofstream outf{ "Sample.txt", std::ios::app };
// If we couldn't open the output file stream for writing
if (!outf)
{
// Print an error and exit
std::cerr << "Uh oh, Sample.txt could not be opened for writing!\n";
return 1;
}
outf << "This is line 3" << '\n';
outf << "This is line 4" << '\n';
return 0;
// When outf goes out of scope, the ofstream
// destructor will close the file
}
现在,如果我们查看 Sample.txt(使用上述示例程序之一打印其内容,或将其加载到文本编辑器中),我们将看到以下内容:
This is line 1
This is line 2
This is line 3
This is line 4
5、使用 open() 显式打开文件
就像可以使用 close() 显式关闭文件流一样,也可以使用 open() 显式打开文件流。 open() 就像文件流构造函数一样工作——它需要一个文件名和一个可选的文件模式。
std::ofstream outf{ "Sample.txt" };
outf << "This is line 1" << '\n';
outf << "This is line 2" << '\n';
outf.close(); // explicitly close the file
// Oops, we forgot something
outf.open("Sample.txt", std::ios::app);
outf << "This is line 3\n";
outf.close();