13.3文件流
<fstream>
定义的
std::ofstream
和
std::ifstream
类提供了文件的输入输出功能.
输出文件流和其他输出流的唯一主要区别在于∶文件流的构造函数可以接收文件名以及打开文件的模式作为参数。默认模式是写文件(ios_base::app
),这种模式从文件开头写文件,改写任何已有的数据。
常量 | 说明 |
---|---|
ios_base::app | 打开文件, 在每一次写操作之前, 移到文件末尾(追加模式) |
ios_base::ate | 打开文件, 打开之后立即移到文件末尾 |
ios_base::binary | 以二进制模式执行输入输出操作(相对于文本模式) |
ios_base::in | 打开文件, 从开头开始读取 |
ios_base::out | 打开文件, 从开头开始写入, 覆盖已有的数据 |
ios_base::trunc | 打开文件, 并删除(截断)任何已有数据 |
上述选项可以组合使用, 用|
分隔开即可
ios_base::app | ios_base::in | ios_base::ate
ifstream
默认ios_base::in
模式, ofstream
默认ios_base::out
模式
1.文本模式与二进制模式
略
2.通过seek()和tell()在文件中转移
所有的输入流和输出流都有 seekx()和tellx()方法。seekx()方法允许在输入流或输出流中移动到任意位置。seek(), tell()有好几种形式。
- 对于输入流,这个方法实际上称为
seekg()
,tellg()
(g 表示 get); - 对于输出流, 这个方法实际上称为
seekp()
,tellp()
(p表示put)。
为什么同时存在seekg()
和seekp()
方法,而不是seek()方法?原因是有的流既可以输入又可以输出,例如文件流。在这种情况下,流需要记住读位置和独立的写位置。这也称为双向 I/O(bidirectional I/O)
seekg()
和 seekp()
有两个重载版本。其中一个重载版本接收一个实参∶绝对位置。这个重载版本将定位到这个绝对位置。另一个重载版本接收一个偏移量和一个位置,这个重载版本将定位到距离给定位置一定偏移量的位置。位置的类型为 std::streampos
,偏移量的类型为 std::streamoff
,这两种类型都以字节计数。预定义的3个位置如下:
位置 | 说明 |
---|---|
ios_base::beg | 表示流的开头 |
ios_base::end | 表示流的结尾 |
ios_base::cur | 表示流的当前位置 |
示例:
- (1)将字符串54321输出至文件。
- (2)验证标记在流中的位置5。
- (3)转移到输出流的位置2。
- (4)在位置2输出0,并关闭输出流。
- (5)在文件 test.out 上打开输入流。
- (6)将第一个标记以整数的形式读入。
- (7)确认这个值是否为54021。
ofstream fout { "test.out" };
if(!fout) {
cerr << "Error opening test.out for writing" << endl;
return 1;
}
// 1. Output the string "54321".
fout << "54321";
// 2.Verify that the marker is at position 5.
streampos curPos { fout.tellp() };
if(curPos == 5) {
cout << "Test passed: Currently at position 5" << endl;
} else {
cout << "Test failed: Not at position 5" << endl;
}
// 3. Move to position 2 in the output stream.
fout.seekp(2, ios_base::beg);
// 4. Output a 0 in position 2 and close the output stream.
fout << 0;
fout.close();
// 5. Open an input stream on test.out.
ifstream fin { "test.out" };
if(!fin) {
cerr << "Error opening test.out for reading." << endl;
return 1;
}
// 6. Read the first token as an integer.
int testVal;
fin >> testVal;
if(!fin) {
cerr << "Error reading from file" << endl;
return 1;
}
// 7. Confirm that the value is 54021.
const int expected { 54021 };
if(testVal == expected) {
cout << format("Test passed: Value is {}", expected) << endl;
} else {
cout << format("Test failed: Value is not {} (it was {})",
expected, testVal) << endl;
}
3.将流链接在一起
任何输入流和输出流之间都可以建立链接,从而实现"访问时刷新((flush-on-access)"的行为。换句话说,当从输入流请求数据时,链接的输出流会自动刷新。这种行为可用于所有流,但对于可能互相依赖的文件流来说特别有用。
通过 tie()
方法完成流的链接。要将输出流链接至输入流,对输入流调用tie()
方法,并传入输出流
的地址。要解除链接,需要传入nullptr
。
ifstream inFile { "input.txt" }; // Note: input.txt must exist
ofstream outFile { "output.txt" };
inFile.tie(&outFile);
outFile << "Hello there!";
string nextToken;
inFile >> nextToken;
// outFile HAS been flushed.