では、早速プログラムを見てみましょう。
住所録として保存しておくファイルの名前をJFILEとdefineしています。 このプログラムが存在するディレクトリと同じディレクトリにjfile.txt というファイルができます。// jusho01.cpp #include <iostream.h> #include <fstream.h> #include <string.h> //memset()で必要 #include <conio.h> // getch()で必要 #define JFILE "jfile.txt" class Jusho { struct _tagdata{ char name[64]; char address[256]; char birth_year[16]; char birth_month[8]; char birth_day[8]; char memo[256]; } data; public: Jusho(); int show(); int write(); };
また、ここでは住所録の元になるJushoクラスを定義しています。 単にdataという名前の構造体と、メンバ関数を列記しているだけです。
コンストラクタです。構造体を全部0で埋めて初期化しています。Jusho::Jusho() { memset(&data, 0, sizeof(data)); }
メンバ関数showの中身です。int Jusho::show() { int n, i = 0; ifstream J; J.open(JFILE, (ios::nocreate)); if (J.is_open()) J >> n; else { cout << "住所録データはありません" << endl; return -1; } while (i < n) { J >> data.name; J >> data.address; J >> data.birth_year; J >> data.birth_month; J >> data.birth_day; J >> data.memo; cout << "----------------------" << endl; cout << data.name << endl; cout << data.address << endl; cout << data.birth_year << "年" << data.birth_month << "月" << data.birth_day << "日" << endl; cout << data.memo << endl; cout << "----------------------" << endl; cout << "Hit any key!" << endl; getch(); i++; } J.close(); return 0; }
ifstreamクラスのオブジェクトJを生成します。
まず、JFILE(jfile.txt)をオープンします。JFILEがオープンできなかった時は その旨を表示してreturnします。
さて、openメンバ関数ですが
のようになっています。ifstream::open void open( const char* szName, int nMode = ios::in, int nProt = filebuf::openprot );
szNameにはオープンするファイル名を指定します。
nModeは次の組み合わせで指定します。
ios::in はファイルを入力用にオープンします。(デフォルトです)
ios::nocreate は、ファイルが存在しないときもこれを作りません。
ios::binary は、ファイルをバイナリーでオープンします。
nPortは、ファイルの保護を指定します。 filebuf::sh_compat は、互換共有モードです。
filebuf::sh_none は、排他モードでファイルを共有しません。
filebuf::sh_read は、読み込み共有許可モードです。
filebuf::sh_write は、書き込み共有許可モードです。
ここでは、nocreateでオープンして次にis_openメンバ関数で検査をしています。
ストリームが割り当てられていると、0 以外の値を返します。失敗していると0を返します。ifstream::is_open int is_open() const;
ここでは、ファイルがオープンできているとファイルの最初の部分を読み込んで nに代入しています。
これは、データの個数(何人分か)を示しています。 そして、その人数分だけファイルから構造体に読み出して、画面表示をします。 1人表示すると表示が止まります。何かキーを入力すると次の1人が表示されます。
人数分だけ表示が終わるとファイルをクローズします。
これでクローズします。ifstream::close void close();
writeメンバ関数の中身です。int Jusho::write() { int no_of_data; char yesno[8]; ifstream Jin; ofstream Jout; Jin.open(JFILE, ios::nocreate); if (Jin.is_open() == 0) no_of_data = 0; else { Jin >> no_of_data; Jin.close(); } cout << "氏名--"; cin >> data.name; cout << "住所--"; cin >> data.address; cout << "誕生年--"; cin >> data.birth_year; cout << "誕生月--"; cin >> data.birth_month; cout << "誕生日--"; cin >> data.birth_day; cout << "メモを書き込みますか(1:Yes 0:NO)"; cin >> yesno; if (strcmp(yesno, "1") == 0) { cout << "メモ--"; cin >> data.memo; } else strcpy(data.memo, "メモなし"); Jout.open(JFILE, ios::ate); Jout.seekp(0); Jout << no_of_data + 1 << endl; Jout.seekp(0, ios::end); Jout << data.name << endl; Jout << data.address << endl; Jout << data.birth_year << endl; Jout << data.birth_month << endl; Jout << data.birth_day << endl; Jout << data.memo << endl; Jout.close(); return 0; }
最初にファイルがあるかどうかを検査してファイルがなければno_of_dataに0をセットします。 存在すればファイルの先頭を呼んでこれをno_of_dataにセットします。
次にユーザーから名前等のデータを構造体に入力させます。
入力が終わったら追加モードでファイルをオープンします。 seekp関数でファイルの先頭に移動してデータ数を書き込みます。
seekp関数でファイルの最後に戻って構造体のデータをファイルに書き込みます。
ファイルをオープンする際はateモードでオープンしてください。appモードで オープンするとseekpで先頭に行くことができません。
szNameはファイル名を指定します。ofstream::open void open( constchar* szName, int nMode = ios::out, int nProt = filebuf::openprot );
nModeはモードを指定します。
ios::app は、ファイルの終わりに移動します。ostream::seekp関数によって移動されても 必ずファイルの終わりに移動します。
ios::ate も、ファイルの終わりに移動します。最初の1バイトはファイルの終わりに追加しますが そのあとは現在位置に書き込みます。
ios::in は、同名のファイルが存在していても削除しません。
ios::out はファイルを出力用にオープンします。デフォルトです。
ios::trunc は、同名のファイルが存在するとその内容を破壊します。これはすでに第28章
で出てきました。
ios::nocreate ファイルがなくても作りません。(エラー)
ios::noreplace ファイルが存在するとこれを置き換えません。(エラー)
ios::binary は、バイナリーでオープンします。
nPortについては、ifstream::openの時と同じです。
また、このプログラム実行時の注意としては半角のスペースや改行は読み込めないので これを入力すると動作がおかしくなります。名前とか住所の途中に半角スペースは 入れないでください。また、メモ欄では改行はできません。
さて、seekpですがこれはostreamクラスで定義されています。ofstreamクラスは ostreamクラスから派生しています。
posは新しい位置を指定します。streamposというデータ型は単なるlong型です。ostream::seekp ostream& seekp( streampos pos ); ostream& seekp( streamoff off, ios::seek_dir dir );
offはオフセット値です。streamoffというデータ型も単なるlong型です。
dirは移動方向で次の中から指定します。
ios::begは、ストリームの先頭から移動
ios::curは、ストリームの現在位置から移動
ios::endは、ストリームの終わりから移動
では、main関数を見てみましょう。
これは、簡単ですね。選択番号noに0が選択されるまでループします。int main() { Jusho j; int no, cont = 1; while (cont) { cout << endl; cout << "*******************" << endl; cout << "1.住所録を見る" << endl; cout << "2.書き込む" << endl; cout << "0.終了" << endl; cout << "*******************" << endl; cout << "番号選択---"; cin >> no; switch (no) { case 0: cont = 0; break; case 1: j.show(); break; case 2: j.write(); break; default: cout << "番号が違います" << endl; break; } } return 0; }
0が選択されたらcontが0になって、ループを抜けてプログラムが終了します。
1が選択されたらshowメンバ関数が呼ばれて、ファイルの中身を見ます。
2が選択されたらwriteメンバ関数が呼ばれて、ファイルに書き込みを行います。
これで、非常に原始的な住所録ができました。
いろいろ不満の多いプログラムですが、これを少しずつ改良していきましょう。
Update Oct/22/2000 By Y.Kumei