3 - 文件流fstream

<fstream>头文件中定义了输入和输出文件流:std::ifstream提供了文件流的输入功能,因为是文件 -> 内存;std::ofstream提供了文件流的输出功能,因为是内存 -> 文件;此外,还有综合两者的双向文件流std::fstream

文件流

构造

文件流的构造函数可以接收文件名以及打开文件的模式作为参数:

// 使用默认构造函数, 并打开文件
ifstream ifs;
ifs.open("test.txt");

// 构造对象时顺便打开文件
ifstream ifs("test.txt");

// 还能指定它打开文件的模式
ifsteam ifs("test.txt", ios_base::ate)

下表是可以指定给文件流的文件打开模式:

常量说明
ios_base::app以追加模式写文件,即每次写入前,将写入位置移到文件末尾
ios_base::ate打开文件后,马上将位置移到文件末尾(仅一次)
ios_base::binary以二进制模式读写文件
ios_base::in打开文件,从开头开始读取。是ifstream的默认模式。
ios_base::out打开文件,从开头开始写入,并覆盖已有数据。是ofstream的默认模式。
ios_base::trunc打开文件,并删除已有数据。

二进制模式和文本模式的不同:二进制模式就是逐字节读写,读下啥就是啥;文本模式中,对行结束符会有一些转换操作,例如Windows的换行是\r\n,而Unix的换行是\n,读/写文件前需要进行转换。

这些模式也能通过|组合:

ios_base::out | ios_base::binary | ios_base::trunc

标准读写

和用户流的标准读写类似,文件流的标准读写模板如下,客户输入文件名,有这个文件就输出文件内容,没有就报错:

string fileName;
string fileContent;

while (cin >> fileName, !cin.eof())
{
	if (cin.bad())
	{
		throw runtime_error("Cin is corrupted");
	}

	ifstream ifs(fileName);
	if (ifs.is_open())
	{
		while (getline(ifs, fileContent))
		{
			cout << fileContent << endl;
		}
		if (ifs.bad())
		{
			throw runtime_error("Ifs is corrupted");
		}
		ifs.close();
	}
	else
	{
		ifs.clear();
		ifs.ignore(numeric_limits<streamsize>::max(), '\n');
		cerr << "File: " << fileName << " does not exist!" << endl;
	}
}

位置操纵

就像C语言中可以通过fseek()等函数操纵文件指针那样,所有的输入输出流都有seekx()tellx()方法。

seekx()

对于输入流,这个方法实际上是seekg()(g表示get);对于输出流,这个方法实际上是seekp()(p表示put)。seekx()有两个重载版本:

  • 接收一个实参——绝对位置,并将流的读/写位置定位到这里。

  • 接收一个偏移量和一个位置,将流的读/写位置定位到 该位置+偏移量 的地方。其中,位置的定义如下:

    位置说明
    ios_base::beg流的开头
    ios_base::end流的末尾
    ios_base::cur流的当前位置

使用例:

// 类型(std::streamoff, std::streampos)
outStream.seekp(2, ios_base::beg);
inStream.seekg(-3, ios_base::end);

tellx()

查询流当前的读/写位置.

流的链接

通过流的链接操作可以实现 访问即刷新(flush-on-access) 的效果,即当从输入流请求数据时,链接的输出流会自动刷新。例如cincout,每当从cin输入数据时,cout都会自动刷新。

可以通过tie()方法完成流的链接:

ifstream inFile("a");
ofstream outFile("b");

// 将输入流链接到输出流
inFile.tie(&outFile);
// 输出流里输出点东西, 此时没有刷新
outFile << "123";
// 让输入流触发输出流刷新
inFile >> "456";
// 输出流刷新了

要想解除链接,可以绑定nullptr。两个输出流也能绑定,这样可以维持两个文件的同步。

参考资料

  • 飘零的落花 - 现代C++详解
  • C++20高级编程