基本文件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();