C++ 随机访问文件

时间:2024-03-10 15:44:31

顺序访问文件

  一个文件是由一个字节序列构成的。操作系统维护一个叫做文件指针(file pointer)的特殊标记,指向序列中的某个位置。读写操作都是在文件指针指向的位置进行。当文件打开时,文件指针被设置在文件开始位置。当读写数据时,文件指针会移动到下一个数据项。例如,如果使用get()函数读取一个字节,C++从文件指针指向的位置读出一个字节,文件指针会向前移动一个字节,如下图所示:

(注:前是向文件结尾的方向,而后是向文件开始的方向)

 

  我们之前写过的程序都是顺读写数据的,即文件指针一直向前移动,这被称为顺序访问文件。如果一个文件以输入的方式打开,将从其文件开始位置向文件结尾读取数据。如果一个文件以输出方式打开,则从其开始位置(默认)或末尾位置(ios::app)开始一个接一个地写入数据项。

随机访问文件

  顺序访问的问题在于,如果我们想要读取一个特定位置的字节,那么必须读取它前面所有的字节,这样做效率太低了。所以C++引入了随机访问文件的概念,也就是说可以任意地向前或向后移动文件指针,而使用的函数是seekp和seekg函数。

seekp,即seek put,用于输出流。

seekg,即seek get,用于输入流。

 

seekp和seekg都有两个版本,分别是有一个参数的和有两个参数的。

一个参数的:参数指出绝对位置,例如:

input.seekg(0);
output.seekp(0);

以上两句代码都将文件指针移动到了文件开始的位置。

两个参数的:第一个参数是长整数,指出偏移量,正数为向前,负数为向后;第二个参数被称为定位基址(seek base),指出偏移量是相对于哪个位置而偏移的。下表给出了三个支持的定位基址参数。

定位基址 描述
ios::beg 偏移量相对于文件开始位置
ios::end 偏移量相当于文件结尾位置
ios::cur 偏移量相当于文件指针当前位置

下表给出一些使用seekp和seekg函数的例子。

语句 描述
seekg(100, ios::beg); 将文件指针移动到从文件开始第100个字节处
seekg(-100, ios::end); 将文件指针移动到文件末尾向后100个字节处
seekp(42, ios::cur); 将文件指针从当前位置向前移动42个字节
seekp(-42, ios::cur); 将文件指针从当前位置向后移动42个字节
seekp(100); 将文件指针移动到文件第100个字节处

我们可以使用tellp和tellg函数返回文件指针的当前位置。

下面给出一个例子。这个例子先将4个Student对象写入到student.dat文件中。然后从student.dat文件中读取第三个同学的信息(一个Student对象占据的空间是12,int=4,char=1,但是char占4):

#include <iostream>
#include <fstream>

using namespace std;
class Student {
public:
    Student(){}
    Student(char name, int age, int score){
        this->age = age;
        this->name = name;
        this->score = score;
    }
    int getAge() const{
        return this->age;
    }
    char getName() const{
        return this->name;
    }
    int getScore() const{
        return this->score;
    }
private:
    int age;
    char name;
    int score;
};

void displayStudent(const Student student){
    cout << "学生" << student.getName() << "的年龄是" << student.getAge() << ", 成绩是" << student.getScore() << endl;
}
int main()
{
    fstream binaryio;
    binaryio.open("student.dat", ios::out|ios::binary);

    Student student1(\'A\', 10, 10);
    Student student2(\'B\', 11, 20);
    Student student3(\'C\', 10, 30);
    Student student4(\'D\', 12, 100);

    binaryio.write(reinterpret_cast<char*>(&student1),sizeof(Student));
    binaryio.write(reinterpret_cast<char*>(&student2),sizeof(Student));
    binaryio.write(reinterpret_cast<char*>(&student3),sizeof(Student));
    binaryio.write(reinterpret_cast<char*>(&student4),sizeof(Student));

    binaryio.close();

    binaryio.open("student.dat", ios::in|ios::binary);

    Student studentNew;

    binaryio.seekg(2*sizeof(Student));

    cout << "当前文件指针的位置是" << binaryio.tellg() << endl;

    binaryio.read(reinterpret_cast<char*>(&studentNew),sizeof(Student));

    displayStudent(studentNew);

    cout << "当前文件指针的位置是" << binaryio.tellg() << endl;
    binaryio.close();

    return 0;
}

运行结果:

student.dat文件: