C++基础教程面向对象(学习笔记(107))

随机文件I / O

文件指针

每个文件流类都包含一个文件指针,用于跟踪文件中的当前读/写位置。当从文件中读取或写入某些内容时,读取/写入将发生在文件指针的当前位置。默认情况下,打开文件进行读取或写入时,文件指针将设置为文件的开头。但是,如果在附加模式下打开文件,则文件指针将移动到文件末尾,因此写入不会覆盖文件的任何当前内容。

使用seekg()和seekp()进行随机文件访问

到目前为止,我们所做的所有文件访问都是顺序的,也就是说,我们已按顺序读取或写入文件内容。但是,也可以进行随机文件访问,也就是说,跳过文件中的各个点来读取其内容。当您的文件已满,并且您希望检索特定记录时,这可能很有用。您可以直接跳到要检索的记录,而不是在找到所需记录之前读取所有记录。

通过使用seekg()函数(用于输入)和seekp()函数(用于输出)操作文件指针来完成随机文件访问。如果你想知道,g代表“get”而p代表“put”。对于某些类型的流,seekg()(更改读取位置)和seekp()(更改写入位置)独立运行,但是,对于文件流,读取和写入位置始终相同,因此seekg和seekp可以是可互换使用。

seekg()和seekp()函数有两个参数。第一个参数是一个偏移量,用于确定移动文件指针的字节数。第二个参数是Ios标志,指定偏移参数应偏移的内容。

Ios seek flag 含义
beg 偏移量相对于文件的开头(默认)
cur 偏移量相对于文件指针的当前位置
end 偏移量相对于文件末尾

正偏移意味着将文件指针移向文件末尾,而负偏移意味着将文件指针移向文件的开头。

这里有些例子:

inf.seekg(14, ios::cur); //向前移动14个字节 
inf.seekg(-18, ios::cur); //向后移动18个字节  
inf.seekg(22, ios::beg); // 在文件中移动到第22个字节
inf.seekg(24); // 在文件中想移动到第24个字节
inf.seekg(-28, ios::end); // 移到文件结尾前的第28个字节

移动到文件的开头或结尾很容易:

inf.seekg(0, ios::beg); // 移到文件的开头
inf.seekg(0, ios::end); // 移到文件末尾

让我们使用seekg()和我们在上一课中创建的输入文件做一个例子。该输入文件如下所示:

This is line 1
This is line 2
This is line 3
This is line 4
这是一个例子:

int main()
{
    using namespace std;
 
    ifstream inf("Sample.dat");
 
    //如果我们无法打开输入文件流进行读取
    if (!inf)
    {
        // 打印错误并退出
        cerr << "Uh oh, Sample.dat could not be opened for reading!" << endl;
        exit(1);
    }
 
    string strData;
 
    inf.seekg(5); // 移动到第5个字符
    // 获取剩余的内容并打印出来
    getline(inf, strData);
    cout << strData << endl;
 
    inf.seekg(8, ios::cur); // 将8个字节移动到文件中
    // 获取剩余的内容并打印出来
    getline(inf, strData);
    cout << strData << endl;
 
    inf.seekg(-15, ios::end); // 在文件末尾向前移动15个字节
    // 获取剩余的内容并打印出来
    getline(inf, strData);
    cout << strData << endl;
    
	return 0;
}

这会产生结果:

is line 1
line 2
his is line 4
注意:一些编译器在与文本文件结合使用时(由于缓冲)具有seekg()和tellg()的错误实现。如果您的编译器是其中之一(并且您将知道因为您的输出将与上面的不同),您可以尝试以二进制模式打开文件:

ifstream inf("Sample.dat", ifstream::binary);

另外两个有用的函数是tellg()和tellp(),它们返回文件指针的绝对位置。这可用于确定文件的大小:

ifstream inf("Sample.dat");
inf.seekg(0, ios::end); // 移到文件末尾
cout << inf.tellg();

这打印:

64
这是sample.dat以字节为单位的长度(假设在最后一行之后返回一个回车符)。

使用fstream同时读取和写入文件

fstream类能够同时读取和写入文件!这几乎是这里最大的警告,不可能在任意读写之间切换。一旦发生读或写,在两者之间切换的唯一方法是执行修改文件位置的操作(例如,搜索)。如果你实际上不想移动文件指针(因为它已经在你想要的位置),你可以随时寻找当前位置:

//假设iofile是fstream类型的对象
iofile.seekg(iofile.tellg(), ios::beg); // 寻求当前的文件位置

如果你不这样做,可能会发生任何奇怪和奇怪的事情。

注意:虽然看起来似乎iofile.seekg(0, ios::cur)也可行,但似乎有些编译器可能会对此进行优化。

另一个棘手的问题:与ifstream不同,我们可以说while (inf)确定是否有更多要阅读,这对fstream不起作用。

让我们使用fstream做一个文件I / O示例。我们将编写一个程序来打开文件,读取其内容,并将它找到的任何元音更改为“#”符号。

int main()
{
    using namespace std;
 
	//注意我们必须同时指定in和out,因为我们正在使用fstream
    fstream iofile("Sample.dat", ios::in | ios::out);
 
    // 如果我们无法打开iofile,请输出错误
    if (!iofile)
    {
        // 打印错误并退出
        cerr << "Uh oh, Sample.dat could not be opened!" << endl;
        exit(1);
    }
 
    char chChar; //我们将按字符分类
 
    // 虽然仍有数据需要处理
    while (iofile.get(chChar))
    {
        switch (chChar)
        {
            //如果我们找到一个元音
            case 'a':
            case 'e':
            case 'i':
            case 'o':
            case 'u':
            case 'A':
            case 'E':
            case 'I':
            case 'O':
            case 'U':
 
                // 备份一个character
                iofile.seekg(-1, ios::cur);
 
                // 因为我们进行了搜索,所以我们现在可以安全地进行写操作了
                //让我们在元音上写一个#
                iofile << '#';
 
                //现在我们想回到读取模式,以便下次调用
                // get()将正确执行. 我们将seekg()用于当前位置,
                // 因为我们不想移动文件指针。
                iofile.seekg(iofile.tellg(), ios::beg);
 
                break;
        }
    }
 
    return 0;
}

其他有用的文件功能

要删除文件,只需使用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会导致您遇到麻烦。

规则:不要将地址写入文件。当您从磁盘读回其值时,最初位于这些地址的变量可能位于不同的地址,并且地址将无效。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章