順序訪問文件
一個文件是由一個字節序列構成的。操作系統維護一個叫做文件指針(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文件: