随机文件I/O
本节阅读量:文件指针
每个文件流类都包含一个文件指针,用于跟踪文件中的当前读/写位置。当从文件中读取或写入内容时,读取/写入会发生在文件指针的当前位置。默认情况下,打开文件进行读取或写入时,文件指针会设置到文件开头。然而,如果以追加模式打开文件,则文件指针会移动到文件末尾,以便写入不会覆盖文件已有内容。
使用seekg()和seekp()随机访问文件
到目前为止,我们所做的所有文件访问都是顺序的——也就是说,我们按顺序读取或写入文件内容。然而,也可以进行随机文件访问——即跳过文件中的一部分,从后续位置开始读写。当文件中包含许多记录,而您希望检索特定记录时,这非常有用。您可以直接跳到要检索的记录,而不是读取所有记录。
随机文件访问是通过使用seekg()函数(用于输入)和seekp()函数(用于输出)操作文件指针完成的。g代表“get”,p代表“put”。对于某些类型的流,seekg()(更改读取位置)和seekp()(更改写入位置)是独立操作——然而,对于文件流,读取和写入位置总是相同的,因此seekg和seekp可以互换使用。
seekg()和seekp()函数接受两个参数。第一个参数是偏移量,它决定文件指针要移动的字节数。第二个参数是一个ios标志,用于指定偏移量参数的相对位置。
| Ios seek 标记 | 含义 |
|---|---|
| beg | 偏移量相对于文件开头(默认行为) |
| cur | 偏移量相对于当前文件指针的位置 |
| end | 偏移量相对于文件结尾 |
正偏移量表示将文件指针移向文件末尾的方向,而负偏移量表示使文件指针移向文件开头的方向。
下面是一些示例:
|
|
移动到文件的开头或结尾很容易:
|
|
警告
在文本文件中,定位到文件开头以外的位置可能会导致意外行为。
在编程中,换行符(“\n”)实际上是一种抽象。
- 在Windows上,新行表示为连续的CR(回车)和LF(换行)字符(因此需要2个字节的存储)。
- 在Unix上,新行表示为LF(换行)字符(因此需要1个字节的存储)。
根据文件的编码方式,跨越换行所需的字节数在任一方向上都可能不同,这意味着结果会因使用的编码而异。
同样,在某些操作系统上,文件可能会用尾随的‘零’(值为0的字节)填充。定位到文件结尾(或相对于文件结尾的偏移量)时,此类文件可能产生不同结果。
为了让您了解它们如何工作,我们使用seekg()和上一课中创建的输入文件做一个示例。该输入文件如下所示:
|
|
下面是一个例子:
|
|
这会产生以下结果:
|
|
根据文件的编码方式,第三行可能会得到不同的结果。
seekg()和seekp()更适用于二进制文件。您可以通过以下方式以二进制模式打开上述文件:
|
|
另外两个有用的函数是tellg()和tellp(),它们会返回文件指针的绝对位置。这可用于确定文件大小:
|
|
在作者的机器上,此命令打印:
|
|
这是sample.txt的长度,以字节为单位(假设最后一行后面有一个换行符)。
注
上一示例中的结果64发生在Windows上。如果在Unix上运行该示例,则会得到60,因为换行符更短。如果文件用尾随的零字节填充,则可能会得到其他结果。
使用fstream同时读取和写入文件
fstream类能够同时读取和写入文件!这里最大的注意事项是,不能在读取和写入之间任意切换。一旦发生读取或写入,在两者之间切换的唯一方法是执行修改文件指针位置的操作(seek)。如果您实际上不想移动文件指针(因为它已经位于想要的位置),则可以始终seek到当前位置:
|
|
如果不这样做,可能会发生许多奇怪的问题。
(注意:尽管“iofile.seekg(0, std::ios::cur)”似乎也可以工作,但有些编译器可能会对此进行优化)。
还有一点小技巧:与ifstream不同,我们不能用“while (inf)”来确定fstream是否还有更多内容可读。
让我们使用fstream做一个文件I/O示例。我们将编写一个程序,打开一个文件,读取其内容,并将找到的任何元音更改为“#”符号。
|
|
运行上述程序后,Sample.txt文件将如下所示:
|
|
其他有用的文件功能
要删除文件,只需使用remove()函数。
此外,如果流当前处于打开状态,is_open()函数将返回true,否则返回false。
关于将指针写入磁盘的警告
虽然将变量流式写入文件相当容易,但处理指针时,事情会变得更加复杂。请记住,指针只是保存它所指向变量的地址。尽管可以将地址读写到磁盘,但这样做极其危险。这是因为变量的地址可能因每次执行而不同。因此,尽管将地址写入磁盘时,变量可能位于地址0x0012FF7C,但当您读回该地址时,它可能已经不在这个地址上了!
例如,假设您有一个名为nValue的整数,其地址为0x0012FF7C。您将nValue赋值为5。您还声明了一个名为*pnValue的指针,该指针指向nValue。pnValue保存nValue的地址0x0012FF7C。您希望保存这些内容以备以后使用,因此将值5和地址0x0012FF7C写入磁盘。
几周后,再次运行该程序,并从磁盘读取这些值。值5被读入另一个名为nValue的变量,而该变量的地址为0x0012FF78。地址0x0012FF7C被读取到名为*pnValue的新指针中。由于当前nValue位于0x0012FF78,而pnValue现在指向0x0012FF7C,因此pnValue不再指向nValue,尝试访问pnValue将导致问题。
警告
不要将内存地址写入文件。当您从磁盘读回它们时,最初位于这些地址的变量可能已经位于不同地址,而原地址可能无效。