28-6 基本文件 IO
C++ 中的文件 I/O 与普通 I/O 非常相似(只是略微复杂一些)。C++ 中有三个基本的文件 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>
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 的文件。用文本编辑器打开它,你会发现它确实包含了我们写入文件的两行内容。

:! cat Simple.txt

请注意,也可以使用 put() 函数向文件中写入单个字符。
文件输入
现在,我们将把上一个例子中写入的文件从磁盘读出来。注意,如果到达文件末尾(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.txt could not be opened for reading!\n";
return 1;
}
// While there's still stuff left to read
std::string strInput{};
while (inf >> strInput)
std::cout << strInput << '\n';
return 0;
// When inf goes out of scope, the ifstream
// destructor will close the file
}
这将产生以下结果:

嗯,这跟我们想要的结果不太一样。记住,提取运算符会遇到空格而中断。为了读取整行,我们需要使用 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
std::string strInput{};
while (std::getline(inf, strInput))
std::cout << strInput << '\n';
return 0;
// When inf goes out of scope, the ifstream
// destructor will close the file
}
这将产生以下结果:

缓冲输出
C++ 中的输出可以缓冲。这意味着输出到文件流的任何内容可能不会立即写入磁盘。相反,多个输出操作可能会被批量处理。这样做主要是为了提高性能。当缓冲区的内容被写入磁盘时,这称为刷新缓冲区。刷新缓冲区的一种方法是关闭文件——缓冲区的内容将被刷新到磁盘,然后文件将被关闭。
缓冲通常不会造成问题,但在某些情况下,它可能会给粗心大意的人带来麻烦。主要原因是当缓冲区中有数据时,程序立即终止(无论是崩溃还是调用 exit())。在这种情况下,文件流类的析构函数不会执行,这意味着文件永远不会关闭,缓冲区也永远不会被刷新。这样一来,缓冲区中的数据就不会被写入磁盘,而是永久丢失。因此,在调用 exit() 之前显式关闭所有打开的文件始终是一个好习惯。
可以使用 ostream::flush() 函数手动刷新缓冲区,或者向输出流发送 std::flush 指令。这两种方法都有助于确保缓冲区的内容在程序崩溃时立即写入磁盘。
值得注意的是,std::endl; 也会刷新输出流。因此,过度使用 std::endl(导致不必要的缓冲区刷新)在执行刷新开销较大的缓冲 I/O 操作(例如写入文件)时,可能会影响性能。出于这个原因,注重性能的程序员通常会使用 \n 而不是 std::endl 来向输出流中插入换行符,以避免不必要的缓冲区刷新。
文件模式
如果我们尝试写入一个已存在的文件会发生什么?再次运行输出示例会发现,每次运行程序时,原始文件都会被完全覆盖。那么,如果我们想在文件末尾追加一些数据呢?原来,文件流构造函数接受一个可选的第二个参数,用于指定文件的打开方式。这个参数称为模式(mode),它接受的有效标志位于 ios 类中。
| Ios文件模式 | 意义 |
|---|---|
| app | 以追加模式打开文件 |
| ate | 读取/写入之前,先定位到文件末尾。 |
| binary | 以二进制模式(而非文本模式)打开文件 |
| in | 以读取模式打开文件(ifstream 的默认设置) |
| out | 以写入模式打开文件(ofstream 的默认模式) |
| trunc | 如果文件已存在,则将其删除。 |
可以通过按位或运算(使用 | 运算符)指定多个标志。ifstream 默认使用 std::ios::in 文件模式。ofstream 默认使用 std::ios::out 文件模式。而 fstream 默认使用 std::ios::in | std::ios::out 文件模式,这意味着默认情况下可以同时进行读写操作。
提示:
由于 fstream 的设计特性,如果使用 std::ios::in 模式打开的文件不存在,则 fstream 可能会失败。如果需要使用 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 文件(使用上面提供的示例程序之一来打印其内容,或者将其加载到文本编辑器中),我们将看到以下内容:

使用 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();
您可以在这里找到有关 open() 函数的更多信息。

浙公网安备 33010602011771号