14.8.7 文件定位指针和随机读取
每个流(不仅仅是文件流,也包括普通流)都隐含着一个指针,对文件来说,C++把每一个文件都看成一个有序的字节流,如图14.5所示,每一个文件或者以文件结束符(End Of File Marker)结束,或者在特定的字节号处结束。
图 14.5 文件字节流示意图
文件是和流关联的,对文件进行读写实际上受流中的文件定位指针(File Position Pointer)的控制,指针标记文件操作的当前位置,该指针在哪里读写操作就在哪里进行。
输入流(istream及其派生类,如ifstream等)的指针称为读指针(Get Pointer),每次抽取操作自动将读指针向流尾移动。输出流(ostream及其派生类,如ofstream等)指针称写指针(Put Pointer),每次插入操作自动将写指针向流尾移动。输入输出流(iostream及其派生类,如fstream等),同时继承了读指针和写指针。
可以通过使用以下成员函数来读出或配置这些指向流中读写位置的流指针。
1.tellg函数和tellp函数
这两个成员函数不用传入参数,返回pos_type类型的值(根据ANSI-C++标准),就是一个long整数,代表当前get流指针的位置(用tellg)或put流指针的位置(用tellp)。
2.seekg函数和seekp函数
这对函数分别用来改变get流指针和put流指针的位置,返回的是流对象的引用,两个函数都被重载为两种不同的原型,如下所述。
❑绝对位置参数
seekg(pos_type position); seekp(pos_type position);
使用这个原型,流指针被改变为指向从流开始计算的一个绝对位置。要求传入的参数类型与tellg函数和tellp函数的返回值类型相同。
❑相对位置参数
seekg(off_type offset,seekdir direction); seekp(off_type offset,seekdir direction);这个原型将put流指针(seekp函数)或get流指针(seekg函数)改变为从“参数direction指定指针”开始计算,位移为offset处的位置。seekdir是在ios_base类中定义的枚举结构,如下所示。 enum seekdir { beg=0,//begin,从流开始计算的offset cur=1,//current,从指针当前位置开始计算的offset end=2//end,从流末尾开始计算的offset };
offset为正值时,代表指针向流尾方向移动,offset也可以为负值,此时,指针向流的头部移动。
对文本模式而言,其中的某些特殊字符(如换行符)可能被修改,不好定位,所以随机访问多用于二进制文件,如示例代码14.30所示,该代码用以获得一个二进制文件的长度。
代码14.30 二进制文件的随机读取RandomAccess
<————————————文件名:example1430.cpp———————————————> 01 #include<iostream> 02 #include<fstream> 03 using namespace std; 04 main() 05 { 06 ofstream out("test. dat",ios_base:out|ios_base:binary);//创建ofstream流对象out 07 if(!out. is_open())//判断文件关联是否成功 08 { 09 cout<<"文件打开失败"; 10 return 0; 11 } 12 float f1=3. 141592f; 13 out. write((char*)&f1,sizeof(f1));//以二进制形式写入test.dat中 14 out. close();//关闭流 15 float f2=0; 16 ifstream in("test. dat",ios_base:in|ios_base:binary); 17 //创建ifstream流对象in,以便从test.dat中读取信息 18 if(!in. is_open())//判断文件关联是否成功 19 { 20 cout<<"读取文件失败"; 21 } 22 in. seekg(0,ios_base:end);//将指针移到流尾 23 long num=in. tellg();//输出当前指针 24 cout<<num<<endl;//标准输出,屏幕显示 25 in. close();//关闭流 26 return 1; 27 }
输出结果如下所示。
4
【代码解析】本代码的前半部分和代码14.29相同,将浮点数3.141592写入到二进制文件test.dat中,为了获得test.dat的大小,采取了先移动指针到流尾,即代码第22行,再输出其值的方式,本例很好地演示了如何获取当前指针以及移动指针。