9.6 可移植的数据文件
通常,我们都希望数据文件可以是跨平台可移植的。为了能够得到这种可移植性,数据文件应该以与写入时相同的方式来读取。例如,考虑下面的结构体:
- struct X{
- char c1;
- char c2;
- }
假设我们如下把一个X类型的值写入一个文件:- X x;
- ofstream f("datafile");
- f.write((const char*)&x, sizeof(x));
接下来,我们使用相应的读取函数,并使用相同的类型来读取这个值:- X x;
- ifstream f("datafile");
- f.read((char*)&x, sizeof(x));
这个实现方法保证了:如果可以符合下面的条件,那么读操作就可以读取被写入的值。
使用同一个编译系统编译包含读操作和写操作的程序,或者编译器将会产生具有相同排列方式和补全方式的目标文件。
在同一个平台执行读操作和写操作。
读操作和写操作都可以成功运行。
然而,这个实现方法并没有保证在一个平台编写的文件一定可以在另外的平台正确地被读取。如果在两个平台中,X的外部描述是不同的,那么读操作将不能得到正确的写入值。
一种实现数据文件跨平台可移植的方法是,按照预先定义的顺序,每次都只读取或者写入一个字节。例如,如果我们如下写入X对象:
- X x;
- ofstream f("datafile");
- f.write(&x.c1, 1);
- f.write(&x.c2, 1);
我们如下读取X:- X x;
- ifstream f("datafile");
- f.read(&x.c1, 1);
- f.read(&x.c2, 1);
那么对于每个和平台P具有相同char类型值的外部描述的平台,都可以移植在平台P写入的数据文件。当然,如果X包含了任何非char类型的数据成员,那么我们就必须递归地、每次一个字节地、按照原先预定顺序写入或者读取这个数据成员(见练习9.2到练习9.4)。
然而,对可移植文件而言,这种一次一个字节的技术却会影响运行时间(即效率)。显然,下面代码
- f.write((const char*)&x, sizeof (x));
可能会比这些代码具有更快的效率: - f.write((const char*)&x.c1, l);
- f.write((const char*)&x.c2, 1);
实际上,一次一个字节写入和读取的额外开销是很小的,通常不会给这种技术的应用带来很大的障碍。