文本文件 I/O


在 C++ 要读写文件,是将之连结至串流,基于串流的 I/O 架构与相关说明,可以在〈Input/output library〉找到。

在〈终端输入输出〉中,谈过coutostream实例,cinistream实例,这两个实例是定义在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>特化版本,用来进行文本文件输出串流操作,ifstreamofstream定义在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的数据,就直接以对应的字节写出。

因为ifstreamofstream各是istreamostream的子类,>><<运算符也可以用在ifstreamofstream实例上,结果就是使用ifstreamofstream时,可以如同使用cincout一样地操作。

来看个读写文件的范例:

#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; 
}

因为ifstreamofstream各是istreamostream的子类,cincout也各是istreamostream实例,因此printread对它们来说是通用的,执行过后,account.data 文件中会存有「123-456-789 Justin 0」,而最后标准输出中,也会显示「123-456-789 Justin 0」。


展开阅读全文