4.6 在输入输出流中定位
每种类型的输入输出流都有“下一个”字符从哪里来(如果是istream)或到哪里去(如果是ostream)的概念。在某些情况下,需要移动流的位置(通过设置“流指针”给流重新定位)。可以使用两种方式进行流定位:一种是使用称为streampos的流指针进行绝对流位置定位;另一种方式和标准C中用于处理文件的库函数fseek()相似,实现从文件头、文件尾或当前位置移动某个给定的字节数进行相对流定位。
用streampos进行绝对流位置定位,需要先调用一个“告知”函数,以便知道流指针在流中的确切位置:对于ostream调用tellp(),对于istream调用tellg()。(“p”表示“写指针”,“g”表示“读指针”。)这个函数返回一个streampos,稍后当要回到流中定位流指针时要用到它,对ostream对象调用seekp(),对istream对象调用seekg()。
第2种方法是相对定位,使用重载版本的seekp()和seekg()函数。函数的第1个参数是要移动的字符数目:这个数目可正可负。第2个参数是移动方向:
下面是在文件中进行定位的一个例子,但是这里没有C语言中stdio对于在文件中进行定位所做的那些限制。在C++中,可以在任何类型的输入输出流中进行定位(尽管在标准流对象如cin和cout中不允许这样做):
这个程序使用二进制输出流写一首诗到文件中。重新打开ifstream文件后,使用seekg()“获取指针”位置。正如读者所看到的,文件指针可以从文件头、文件尾或从文件当前位置进行搜索。显然,从文件头开始移动指针需要提供一个正数做参数,而从文件尾移动指针需要提供一个负数做参数。
既然已经熟悉了streambuf类以及如何在流中定位,读者就能理解创建一个既能够读文件又能够写文件的流对象的另一种方法了(不使用fstream对象)。下面的代码首先使用某些标记创建一个ifstream,这些标记表明它既能用于文件输入又能用于文件输出。因为不能对ifstream对象进行写入操作,所以需要创建一个具有内置流缓冲区的ostream对象:
读者也许想知道向这两个对象之一写入数据时会发生什么情况。举例如下:
前5行把这个程序的源代码拷贝到一个叫做iofile.out的文件,接着关闭该文件,这样一来,就为读者提供一个安全的文本文件。然后,使用前面介绍的技术创建两个对象,以便对同一个文件进行读和写操作。在语句cout<<in2.rdbuf()中,可以看到“读”指针被初始化为指向文件头。“写”指针被设置为指向文件尾,因为文本信息“Where does this end up?”是追加到文件中的。然而,如果写指针通过调用函数seekp()被移动到指向文件头,新写入的文本将覆盖原来的文本。调用函数seekg()把读指针移回到文件头,就可以分别看到两次写操作后所显示的文件中的内容。当out2超出作用域范围后,系统调用析构函数,使文件自动保存和关闭。