在 C++ 要读写文件,是将之连结至串流,基于串流的 I/O 架构与相关说明,可以在〈Input/output library〉找到。
在〈终端输入输出〉中,谈过cout
是ostream
实例,cin
是istream
实例,这两个实例是定义在iostream
标头。
istream
类型是定义在istream
标头,它是basic_istream
模版类的basic_istream<char>
特化版本,basic_istream
是字符输入串流的基础模版类;ostream
类型是定义在ostream
标头,它是basic_ostream
模版类的basic_ostream<char>
特化版本,basic_ostream
是字符输出串流的基础模版类。
在文本文件串流的处理方面,basic_ifstream
继承了basic_istream
,而ifstream
类型是basic_ifstream<char>
特化版本,用来进行文本文件输入串流操作,basic_ofstream
继承了basic_ostream
,而ofstream
类型是basic_ofstream<char>
特化版本,用来进行文本文件输出串流操作,ifstream
、ofstream
定义在fstream
标头之中。
使用ifstream
创建实例时,可以指定连结的文件名称,如果没有指定文件名称,会创建一个没有连结文件的串流,后续必须以open
来连结文件:
void open( const char *filename,
ios_base::openmode mode = ios_base::in );
void open( const std::string &filename,
ios_base::openmode mode = ios_base::in );
例如,可以使用下面这个片段来开启文件输入串流:
ifstream in;
in.open("filename");
如果开启失败,串流对象在布尔判别场合会是false
,可以使用下面的片段来判断:
if(in) {
... 进行文件处理
}
类似地,使用ofstream
创建实例时,可以指定连结的文件名称,如果没有指定文件名称,会创建一个没有连结文件的串流,后续必须以open
来连结文件:
void open( const char *filename,
ios_base::openmode mode = ios_base::out );
void open( const std::string &filename,
ios_base::openmode mode = ios_base::out );
mode
决定文件的开启模式,是由ios
类定义的常数来决定,下面列出openmode
的值与用途:
- ios::in:输入(basic_ifstream 默认)
- ios::out:写入(basic_ofstream 默认)
- ios::ate:开启后移至文件尾端
- ios::app:附加模式
- ios::trunc:如果文件存在,清除文件内容
- ios::binary:二进制模式
当然,程序的世界实际上并没有文本文件这东西,数据都是二进制,字符串流只是在读取或写入的过程,会进行文本编码的转换,例如int
数字 9,在写入的操作中,会转换为编码 57 的字节数据,至于本身是char
的数据,就直接以对应的字节写出。
因为ifstream
、ofstream
各是istream
、ostream
的子类,>>
与<<
运算符也可以用在ifstream
、ofstream
实例上,结果就是使用ifstream
、ofstream
时,可以如同使用cin
、cout
一样地操作。
来看个读写文件的范例:
#include <iostream>
#include <fstream>
using namespace std;
struct Account {
string id;
string name;
double balance;
Account(string id = "", string name = "", double balance = 0.0) :
id(id), name(name), balance(balance) {};
};
void print(ostream &out, Account &acct) {
out << acct.id << " "
<< acct.name << " "
<< acct.balance;
}
void read(istream &in, Account &acct) {
in >> acct.id >> acct.name >> acct.balance;
}
int main() {
Account acct = {"123-456-789", "Justin Lin", 1000};
ofstream out("account.data");
print(out, acct);
out.close(); // 记得关闭文件
Account acct2;
ifstream in("account.data");
read(in, acct2);
in.close(); // 记得关闭文件
print(cout, acct2);
return 0;
}
因为ifstream
、ofstream
各是istream
、ostream
的子类,cin
与cout
也各是istream
、ostream
实例,因此print
、read
对它们来说是通用的,执行过后,account.data 文件中会存有「123-456-789 Justin 0」,而最后标准输出中,也会显示「123-456-789 Justin 0」。