第67章 成績表をファイルに保存する


前章までのプログラムでは、成績表を保存することができませんでした。 今回は、これをファイルに保存したり、ファイルから読み出したりできるようにします。



すでにファイルに保存してある場合は、メニューの6番を選択します。



メニューの2番を選択すると、確かに成績表が読み込まれているのが わかります。

これに、データを継ぎ足したり、削除したりして保存することができます。

ここでは、簡単のためファイル名は決まったものを使うようにしてあり、 ユーザーがファイル名を入力することはできません。



では、プログラムを見てみましょう。

// vector05.cpp

#define FNAME "seiseki.txt"

#include <iostream>
#include <vector>
#include <string>
#include <fstream>

using namespace std;

int menu();
int input_data();
int show_data();
int shusei();
int del();
int read_file();
int write_file();

vector<int> point;
vector<string> shimei;
ファイルを扱うためfstreamライブラリをインクルードします。(iostreamライブラリが あれば、fstreamライブラリが無くても可。ここでは、ライブラリ名を覚えるためインクルード)

read_file, write_file関数が増えました。

int main()
{
    int menuno, endmark = 0;
    string nm, yesno;

    while (1) {
        menuno = menu();
        switch (menuno) {
            case 1:
                input_data();
                break;
            case 2:
                show_data();
                break;
            case 3:
                shusei();
                break;
            case 4:
                del();
                break;
            case 5:
                write_file();
                break;
            case 6:
                read_file();
                break;
            case 0:
                endmark = 1;
                break;
        }
        if (endmark == 1)
            break;
    }
    return 0;
}
メニューの5,6が増えました。
int menu()
{
    int no;
    while (1) {
        cout << endl;
        cout << "**** MENU ***" << endl;
        cout << "1:データ入力" << endl;
        cout << "2:データ表示" << endl;
        cout << "3:データ修正" << endl;
        cout << "4:データ削除" << endl;
        cout << "5:ファイルに保存" << endl;
        cout << "6:ファイルから読み出し" << endl;
        cout << "0:終了" << endl;
        cout << "---> ";

        cin >> no;
        if (no < 0 || no > 6) {
            cout << endl;
            cout << "番号が不正です" << endl;
            continue;
        }
        break;
    }
    return no;
}
メニューの5,6番が増えています。
int input_data()
{
    string nm;
    int pt;

    while (1) {
        cout << "氏名 = ";
        cin >> nm;
        if (nm == "end")
            break;
        shimei.push_back(nm);
        cout << "得点 = ";
        cin >> pt;
        point.push_back(pt);

        cout << endl;
    }

    return (int)shimei.size();
}

int show_data()
{
    int i, no;

    no = (int)point.size();

    cout << endl;

    for (i = 0; i < no; i++)
        cout << "[" << i << "]" << shimei[i] << "---" << point[i] << endl;

    return 0;
}

    

int shusei()
{
    int no, datano, pt;
    string nm, yesno;

    vector::iterator p;
    vector::iterator q;

    p = point.begin();
    q = shimei.begin();

    datano = (int)point.size();

    while (1) {
        cout << endl;
        cout << "修正するデータの番号 = ";
        cin >> no;
        if (no < 0 || no >= datano) {
            cout << "番号が不正です" << endl;
            continue;
        }
        cout << "氏名 = ";
        cin >> nm;
        cout << "得点 = ";
        cin >> pt;
        q += no;
        p += no;
        *q = nm;
        *p = pt;
        cout << "続けますか(Y/N) -- ";
        cin >> yesno;
        if (yesno == "N")
            break;
    }
    return 0;
}

int del()
{
    int no, datano;
    char yn[8];

    vector::iterator p;
    vector::iterator q;

    p = point.begin();
    q = shimei.begin();

    
    while (1) {
        datano = (int)point.size();

        if (datano == 0) {
            cout << "データがありません" << endl;
            return -1;
        }
        show_data();
        cout << "削除するデータの番号 -- ";
        cin >> no;
        if (no < 0 || no > datano) {
            cout << "番号が不正です" << endl;
            return -2;
        }
        cout << no << "番のデータ(" << shimei[no] << ")を削除しますか(Y/N) -- ";
        cin >> yn;
        if (strcmp(yn, "Y") == 0) {
            p += no;
            point.erase(p);
            q += no;
            shimei.erase(q);
            cout << "削除されました" << endl;
            p = point.begin();
            q = shimei.begin();
        }
        cout << "続けますか(Y/N) -- ";
        cin >> yn;
        if (strcmp(yn, "Y") != 0)
            break;
            
    }
    return 0;
}
これらの関数に変更はありません。
int read_file()
{
    ifstream file_in;
    int data_no, i, p;
    string yn, data, data2;
    const char *lpszStr;

    data_no = (int)shimei.size();

    if (data_no != 0) {
        cout << "現在のデータが無効になりますがよろしいですか(Y/N)--";
        cin >> yn;
        if (yn == "N" || yn == "n")
            return -2;
        //現在のデータをすべて削除
        for (i = 0; i < data_no; i++) {
            shimei.pop_back();
            point.pop_back();
        }
    }

    file_in.open(FNAME);
    if (!file_in.is_open()) {
        cout << "ファイルのオープンに失敗しました" << endl;
        return -1;
    }
    getline(file_in, data);
    lpszStr = data.c_str();
    data_no = atoi(lpszStr);

    for(i = 0; i < data_no; i++) {
        getline(file_in, data);
        shimei.push_back(data);
        getline(file_in, data);
        lpszStr = data.c_str();
        p = atoi(lpszStr);
        point.push_back(p);
    }
    file_in.close();
    return 0;
}
ファイルからデータを読み出すには、ifstreamクラスを使います。

ファイルから読み出す前に、現在すでにデータ入力が行われているかどうかを 調べます。簡単のため、ファイルから読み出す場合は、現在入力されている データは削除して、新たに読み込んだデータのみを使うようにします。

ファイルをオープンするには、ifstreamクラスのopenメンバ関数を使います。

ファイルがオープンされたかどうかを確認するにはis_openメンバ関数を使います。

getline関数でファイルの1行目を読み出します。ここには、データの個数が書かれているもの とします。上のプログラムではstringクラスのdataオブジェクトとして読み込まれます。

さて、これをint型に変換するにはC言語に慣れ親しんだ人にはatoi関数を使いたくなるものです。 しかし、

atoi(data);

などとすることはできません。これには、stringクラスのc_strメンバ関数を使って 文字列へのポインタを取得する必要があります。

次に、データの個数だけファイルから読み出して、push_backします。

データを読み終わったらcloseメンバ関数でファイルをクローズします。

int write_file()
{
    ofstream file_out;
    int data_no, n;

    data_no = (int)point.size();

    if (data_no == 0) {
        cout << "保存すべきデータがありません" << endl;
        return -1;
    }

    cout << data_no << "個のデータがあります" << endl;

    file_out.open(FNAME, ios_base::trunc);

    file_out << (int)shimei.size() << endl;

    for (n = 0; n < data_no; n++) {
        file_out << shimei[n] << endl;
        file_out << point[n] << endl;
    }
    file_out.close();

    
    return 0;
}
ファイルに書き込む関数です。

ofstreamクラスを使います。

openメンバ関数でファイルをオープンします。この時、オープンモードにios_base::trunc を指定して、もしすでにファイルが存在してデータがあった場合は切り詰めるようにします。

データを書き終わったらcloseメンバ関数で、ファイルをクローズします。

さて、今回行ったファイル入出力は第28章で行ったファイル入出力とほとんど同じですね。


[C++Index] [総合Index] [Previous Chapter] [Next Chapter]

Update Jan/29/2003 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。