第38章 住所録を作ろう その1


今回から、住所録作りをやってみます。基本はファイルの入出力です。 すでに第27章で簡単な家計簿作りをやりました。



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

// 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(); };

住所録として保存しておくファイルの名前をJFILEとdefineしています。 このプログラムが存在するディレクトリと同じディレクトリにjfile.txt というファイルができます。

また、ここでは住所録の元になるJushoクラスを定義しています。 単にdataという名前の構造体と、メンバ関数を列記しているだけです。

Jusho::Jusho() { memset(&data, 0, sizeof(data)); }

コンストラクタです。構造体を全部0で埋めて初期化しています。

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; }

メンバ関数showの中身です。

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メンバ関数で検査をしています。

ifstream::is_open int is_open() const;

ストリームが割り当てられていると、0 以外の値を返します。失敗していると0を返します。

ここでは、ファイルがオープンできているとファイルの最初の部分を読み込んで nに代入しています。

これは、データの個数(何人分か)を示しています。 そして、その人数分だけファイルから構造体に読み出して、画面表示をします。 1人表示すると表示が止まります。何かキーを入力すると次の1人が表示されます。

人数分だけ表示が終わるとファイルをクローズします。

ifstream::close void close();

これでクローズします。

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; }

writeメンバ関数の中身です。

最初にファイルがあるかどうかを検査してファイルがなければno_of_dataに0をセットします。 存在すればファイルの先頭を呼んでこれをno_of_dataにセットします。

次にユーザーから名前等のデータを構造体に入力させます。

入力が終わったら追加モードでファイルをオープンします。 seekp関数でファイルの先頭に移動してデータ数を書き込みます。

seekp関数でファイルの最後に戻って構造体のデータをファイルに書き込みます。

ファイルをオープンする際はateモードでオープンしてください。appモードで オープンするとseekpで先頭に行くことができません。

ofstream::open void open( constchar* szName, int nMode = ios::out, int nProt = filebuf::openprot );

szNameはファイル名を指定します。

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クラスから派生しています。

ostream::seekp ostream& seekp( streampos pos ); ostream& seekp( streamoff off, ios::seek_dir dir );

posは新しい位置を指定します。streamposというデータ型は単なるlong型です。

offはオフセット値です。streamoffというデータ型も単なるlong型です。

dirは移動方向で次の中から指定します。

ios::begは、ストリームの先頭から移動
ios::curは、ストリームの現在位置から移動
ios::endは、ストリームの終わりから移動

では、main関数を見てみましょう。

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; }

これは、簡単ですね。選択番号noに0が選択されるまでループします。

0が選択されたらcontが0になって、ループを抜けてプログラムが終了します。
1が選択されたらshowメンバ関数が呼ばれて、ファイルの中身を見ます。
2が選択されたらwriteメンバ関数が呼ばれて、ファイルに書き込みを行います。

これで、非常に原始的な住所録ができました。

いろいろ不満の多いプログラムですが、これを少しずつ改良していきましょう。


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

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