文件读写#
通过 #include <fstream>
中的 ofstream
、ifstream
和 fstream
文件流, 你可以对文件进行写入、读取或读写.
它们的用法于 cout
和 cin
相似, 因此请参考 几种 cin 输入方式的区别 等了解. 以下解释如何打开文件、读取到了文件末尾和关闭文件.
打开文件#
要使用文件流, 我们需要用它先打开一个文件:
1#include <fstream>
2using namespace std;
3
4int main() {
5 ifstream ifile;
6 ifile.open("C:/test.txt", std::ios_base::in);
7
8 // 然后就能像 cin 一样使用 ifstream
9 int value = 0;
10 ifile >> value;
11}
这样就打开了一个文件, 但是何必呢?!
你没必要指明 std::ios_base::in
, ifstream
(input file stream) 始终会给文件打开方式加上它:
1int main() {
2 ifstream ifile;
3 ifile.open("C:/test.txt");
4}
你没必要分出一行写, 可以在声明 ifile
时就让它打开文件:
1int main() {
2 ifstream ifile("C:/test.txt");
3}
- "可是我之前的写法也是可以的啊……"
你如果不是为了读取文件的内容, 何必用
ifstream
? 既然用ifstream
就是想读取文件内容, 那何必再加一句std::ios_base::in
? 你用 ifstream, 就是为了读取文件.你如果不是为了现在打开这个文件, 何必去声明
ifstream ifile
? 既然你声明ifstream ifile
就是要打开文件, 何必再在额外一行去写一个ifile.open
来打开文件? 你声明 ifstream ifile, 就是为了在此时此刻打开文件.
写代码必须注意代码的目的性, 没有明确代码各部分的目的是写不出代码的直接原因之一.
同理, ofstream
会始终在打开方式中加上 std::ios_base::out
, fstream
会始终在打开方式中加上 std::ios_base::in | std::ios_base::out
, 哪怕你给它其他的打开方式.
判断打开成功#
通过 ifile.is_open()
, 我们可以判断上一次打开文件是否成功:
1#include <cstdlib> // for exit, EXIT_FAILURE, EXIT_SUCCESS
2
3int main() {
4 ifstream ifile("C:/test.txt");
5 if (!ifile.is_open()) {
6 cerr << "错误信息\n"; // err 的意思是 error
7 exit(EXIT_FAILURE);
8 }
9}
读取文件到末尾#
在课上可能像下面这样写, 千万千万千万千万 不要用!!!
1int main() {
2 ifstream ifile("C:/test.txt");
3 for (int value = 0; !ifile.eof();) { // 或者有些奇怪的老师会写 ifile.eof() == 0
4 ifile >> value;
5
6 cout << value;
7 }
8}
是, 没错, 这样是可以判断文件到没到文件尾 (eof, end of file), 但是其他问题呢? 例如, 现在循环里是在读取 int
类型, 那如果文件里有一个不是数字的字符呢? 读取将会失败, 你将永远不能达到文件尾, 这个循环将会永远继续下去.
前置内容
在往下看正确的方案之前, 请先阅读 几种 cin 输入方式的区别 直到读完 "共同部分".
数据读写有三类错误状态: eof (到达读取数据的尾部)、fail (读写操作失败)、bad (读写流本身出现不可逆转的问题). 读写操作失败 fail 才是你真正想要的, 它涵盖了 bad 和 eof 的几乎所有情况, 唯一没涵盖的某种 eof 情况也不是你真正想要的.
因此, 我们应该使用 !ifile.fail()
来表达读取有没有结束, 而 几种 cin 输入方式的区别 的共同部分已经说了怎么写:
1int main() {
2 ifstream ifile("C:/test.txt");
3 if (!ifile.is_open()) {
4 cerr << "打开文件失败!\n";
5 exit(EXIT_FAILURE);
6 }
7
8
9 for (int value = 0; ifile >> value;) {
10 cout << value;
11 }
12
13 if (ifile.bad()) {
14 cerr << "读写流本身出现不可逆转的问题!\n";
15 exit(EXIT_FAILURE);
16 } else if (ifile.eof()) {
17 cout << "成功读取到了文件尾!\n";
18 } else if (ifile.fail()) {
19 cerr << "读写操作出现问题!\n";
20 exit(EXIT_FAILURE);
21 }
22}
备注
注意, ifile.good() != !ifile.fail()
, 就像下面表里列的那样, 但别去记这玩意. 上面已经说了, 用 !ifile.fail()
!

关闭文件#
太长别看: 你不需要手动用 file.close()
来关闭文件, 用这种方式只会让人知道你真的不会 C++.
C++ 中的对象存在 生命期和存储周期 的概念. 简单来说, 你在大括号 {}
内声明的变量, 到 }
时就会死掉.
1int main() {
2 int value1 = 0;
3 cin >> value1;
4
5 if (value != 0) {
6 int value2 = value1 * value1;
7 cout << value2;
8 } // value2 死掉了
9
10 cout << value2; // 错误: value2 死都死了, 你不可能用它
11} // value1 死掉了
当 ofstream
、ifstream
和 fstream
死掉时, 它们会自动关闭文件.
1int main() {
2 std::ifstream ifile("C:/test.txt");
3
4 int value = 0;
5 ifile >> value;
6} // ifile 死了, 它自动关闭文件!!!
参见
文件需要我们预先获取、之后释放, 因而是一种 "资源". 在 资源 (resource) 中, 我解释了资源的所有权问题和 C++ 中资源的最佳管理方案——RAII, 这就是 ofstream
、ifstream
和 fstream
采用的方案.
读写同一个文件#
我们打开一个文件对它进行写入时, 是先将数据写入到缓冲区, 再在合适时候 (比如) 一次性从缓冲区写入到文件.
我们打开一个文件对它进行读取时, 是先将文件读入到缓冲区, 再从缓冲区中读取数据.
这意味着, 如果我们先用 ofstream
写入文件, 在 ofstream
还没有关闭文件时, 就用 ifstream
可读取同一文件, 那么可能读取的是原来文件中的数据. (想想你用 Visual Studio 写代码忘了保存, 觉得自己改动没有效果然后发图片问问题. 这就是你没有将缓冲区 (代码编辑区) 的内容写入到文件中.)
"这总能用 file.close() 了吧?" 但你觉得下面的代码方便阅读吗?
你可以人为添加 {}
; 或者往往更好地, 既然是写入和读取是在做不同的事情, 为什么不写成分别的函数?
1int main() {
2 {
3 ofstream ofile("C:/test.txt");
4 ofile << 682132891;
5 }
6 {
7 ifstream ifile("C:/test.txt");
8 int value = 0;
9 ifile >> value;
10 }
11}
不知道 string
是啥? 求求你看天鹅书吧😭
1#include <string> // for string
2using namespace std;
3
4void write_file(string file) {
5 ofstream ofile(file);
6 ofile << 682132891;
7}
8
9void read_file(string file) {
10 ifstream ifile(file);
11 int value = 0;
12 ifile >> value;
13}
14
15int main() {
16 string file = "C:/test.txt";
17 write_file(file);
18 read_file(file);
19}
当然你可以用既能写入又能读取的 fstream
, 但我认为反而过于麻烦了:
1int main() {
2 fstream file("C:/test.txt");
3 file << 682132891;
4
5 file.seekp(0); // 你能看懂这个函数是回到文件开头吗?
6
7 int value = 0;
8 ifile >> value;
9}
二进制文件读写#
二进制文件读写见于 二进制输入输出 (binary I/O).