Categories
不学无术

C++ Gossip: 隨機存取檔案

本文来自:http://openhome.cc/Gossip/CppGossip/RadomAccessFile.html

使用 get 指標或 put 指標,可以自由的移動至檔案中指定的位置進行讀取或寫入的動作,通常隨機存取檔案會使用二進位模式進行,文字模式開啟的檔案並不適合作隨機存取的動作。
如何利用隨機存取來讀寫所有的資料,必須視您的需求而定,需求決定您的資料結構,這邊以一個最簡單的例子來示範隨機存取,寫入檔案時都是使用固定大小的struct,由於資料大小固定,這可以方便明確的指定檔案中讀取的位置。
假設有一個簡單的學生成績資料如下:

  • Student.h
#ifndef DATASTRU_H
#define DATASTRU_H
struct Student {
    int studyNumber;
    char name[80];
    double score;
};
#endif

一個結構的大小是固定的,當要寫入一個結構時,可以使用這樣的語法:

fout.write(reinterpret_cast<const char*> (&student),
            sizeof(Student));


其中student是Student自訂struct所宣告的變數名稱,由於write接受const char*型態的變數,所以使用reinterpret_cast<const char*> 將之轉換為const char*指標。
下面這個程式示範,如何建立一個檔案,當中包括50筆空的資料:

  • main.cpp
#include <iostream>
#include <fstream>
#include "Student.h"
using namespace std;
int main(int argc, char* argv[]) {
    if(argc != 2) {
        cout << "指令: create <filename>" << endl;
        return 1;
    }
    ofstream fout(argv[1], ios::binary);
    if(!fout) {
        cerr << "檔案輸出失敗" << endl;
        return 1;
    }
    int count;
    cout << "要建立幾筆資料? ";
    cin >> count;
    Student student = {0, "", 0.0};
    for(int i = 0; i < count; i++) {
        fout.write(reinterpret_cast<const char*> (&student),
            sizeof(Student));
    }
    fout.close();
    return 0;
}

執行結果:

create data.bin
要建立幾筆資料? 50

接下來可以使用下面這個程式進行隨機存取,使用學號來作資料的位置指定,將之儲存在檔案中的指定位置:

#include <iostream>
#include <fstream>
#include "Student.h"
using namespace std;
int main(int argc, char* argv[]) {
    Student student;
    int count = 0;
    if(argc < 2) {
        cerr << "指令: write <filename>";
        return 1;
    }
    fstream fio(argv[1], ios::in | ios::out | ios::binary);
    if(!fio) {
        cerr << "無法讀取檔案" << endl;
        return 1;
    }
    while(true) {
        fio.read(reinterpret_cast<char *> (&student),
            sizeof(Student));
        if(!fio.eof())
            count++;
        else
            break;
    }
    fio.clear();
    cout << "輸入學號(1-" << count << ")"  << endl
         << "輸入0離開";
    while(true) {
        cout << "n學號? ";
        cin >> student.studyNumber;
        if(student.studyNumber == 0)
            break;
        cout << "輸入姓名, 分數" << endl
             << "? ";
        cin >> student.name >> student.score;
        fio.seekp((student.studyNumber - 1) * sizeof(Student));
        fio.write(reinterpret_cast<const char*> (&student),
            sizeof(Student));
    }
    fio.close();
    return 0;
}

執行結果:

write data.bin
輸入學號(1-50)
輸入0離開
學號? 1
輸入姓名, 分數
? 良葛格 88
學號? 2
輸入姓名, 分數
? 毛妹妹 94
學號? 5
輸入姓名, 分數
? 毛毛蟲 75
學號? 0

接下來可以使用下面這個程式讀取方才所輸入的資料:

#include <iostream>
#include <fstream>
#include "Student.h"
using namespace std;
int main(int argc, char* argv[]) {
    Student student;
    int count = 0, number;
    if(argc != 2) {
        cout << "指令: read <filename>" << endl;
        return 1;
    }
    ifstream fin(argv[1], ios::in | ios::binary);
    if(!fin) {
        cerr << "無法讀取檔案" << endl;
        return 1;
    }
    while(true) {
        fin.read(reinterpret_cast<char *> (&student),
            sizeof(Student));
        if(!fin.eof())
            count++;
        else
            break;
    }
    fin.clear();
    cout << "輸入學號(1-" << count << ")"  << endl
         << "輸入0離開";
    while(true) {
        cout << "n學號? ";
        cin >> number;
        if(number == 0)
            break;
        else if(number > count) {
            cout << "輸入學號(1-" << count << ")" << endl;
            continue;
        }
        cout << "n學號t姓名tt分數" << endl;
        fin.seekg((number - 1) * sizeof(Student));
        fin.read(reinterpret_cast<char*> (&student),
            sizeof(Student));
        cout << student.studyNumber << "t"
             << student.name << "tt"
             << student.score << endl;
    }
    fin.close();
    return 0;
}

執行結果:

read data.bin
輸入學號(1-50)
輸入0離開
學號? 1
學號    姓名            分數
1       良葛格          88
學號? 2
學號    姓名            分數
2       毛妹妹          94
學號? 3
學號    姓名            分數
0                       0
學號? 5
學號    姓名            分數
5       毛毛蟲          75
學號? 0

這幾個程式是隨機存取的簡單示範,您也可以結合起來,製作一個簡易的成績登記程式。
在判斷資料筆數時還有更簡單的方法,就是開啟檔案後先使用ios::end將指標移至檔案尾,然後使用tellg()得到目前的檔案指標位置,再除以資料結構的大小除可得知資料筆數,例如:

file.seekg(0, ios::end);
count = file.tellg() / sizeof(資料結構);
 


 

关闭提示 关闭

确 认 取 消

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.