前章までのプログラムでは、成績表を保存することができませんでした。
今回は、これをファイルに保存したり、ファイルから読み出したりできるようにします。
すでにファイルに保存してある場合は、メニューの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章で行ったファイル入出力とほとんど同じですね。
Update Jan/29/2003 By Y.Kumei