【コンピュータサイエンスの独学】テキストファイルとエラー処理
エラー処理
ファイルに限らずエラー処理は非常に大切です。そして先ほどのプログラムの読み込み時に行っているエラー処理の手法は悪例です。
エラーが発生したか否かはプログラムに於いて最重要項目です。エラーが発生しているにも拘らず何等かの処理をしたとすれば、それが正しく処理を行っているとは限らないからです。
簡単な例を挙げましょう。メモリー確保の malloc は成功すればその領域の先頭アドレスが、失敗すればヌル・ポインターが戻り値です。失敗したか成功したかの判断とその処理を後回しにしたらどうなるか想像してください。もし失敗してヌル・ポインターが戻ってきた場合、そのポインターに対して読み書きを行おうとすればプログラムは正常に動作しません。 OS によって強制終了させられるか、何事も無かったかのようにプログラムは動作し続けしばらくしてからシステムが暴走したり、危険な事この上無しです。
先のプログラムではエラー処理を後回しにして最初にファイルの終端か否かを判断しています。エラーが起こっていてもお構い無し。これがどれほど危険な事か。エラー判断は最初に行いエラーであればそれに応じた処理をします。先のプログラムではエラーでない事を確認した後にファイルの終端かどうかを判断するのが正しいのです。
最初にエラー判断とその処理を行い、正常な処理はその後です。処理の優先順位はエラー対応が最上位です。
従って先のプログラムでは次の様に処理しなければなりません。
fgets 関数の戻り値は無視します。
fgets 関数処理直後に ferror 関数を呼び出しエラー判断を行い適切に処理します。
fgets 関数が正しく処理されていれば feof 関数でファイルの終端かどうかを判断、処理します。
エラーの後はファイルを閉じて終了するだけならこれで良いのですが、一旦失敗したストリームに対しまた別の処理を行う場合もあるでしょう。その様な処理が必要ならエラー処理の最後でストリームのエラーを次の標準関数で初期化します。
ヘッダー・ファイル stdio.h
プロトタイプ void clearerr( FILE * stream );
stream は FILE 型へのポインターです。この関数を実行するとそのストリームに対してエラー及び EOF の状態を初期化します。エラーが起こったストリームに対し何等かの理由で fseek 関数を実行する場合は clearerr 関数を実行する必要があります。
ファイルの操作はストリームのお蔭で難しくありません。しかしエラー処理は色々と手間が掛かります。ここで手を抜くとファイルの内容が意図しないものになったりファイルそのものが壊れたりと目も当てられない状況に陥る恐れがあります。ファイルのエラー処理は厳格に行うよう心掛けてください。
順次アクセスとテキスト・ファイル
テキスト・ファイルの操作は、書き込みは常に先頭から順に、読み込みも常に先頭から順に行っています。ですから順次アクセスです。
今度はテキスト・ファイルのランダム・アクセスとしたいところですがそれは出来ないと考えてください。
テキスト・ファイルは文字列と一部の制御コードで構成されます。また行単位で管理され、一行の終わりは改行コードで示します。つまり行がフィールドであり、レコードは一つのフィールドしか存在しません。各フィールド長は不定なのでレコード長も不定です。読み書きする単位はレコードです。ところがレコードの長さが一定ではないので例えば先頭から三番目のレコードの位置がどこなのかすぐに分かりません。
一次元配列を思い浮かべてください。配列の各要素ごとの大きさは一定です。その為先頭から三番目の要素にアクセスするには「要素の大きさ * ( 三番目 - 1 ) 」で求められます。テキスト・ファイルを配列、レコードを要素に置き換えてください。各要素の大きさは一定ではないので先の計算で求められません。
テキスト・ファイル内の各行の大きさをどこかに記憶すればランダム読み込みも不可能ではありません。しかしそれは読み込みに対してだけで、書き込みが関わると無理です。
全部で三行のテキスト・ファイルの二行目を書き換えるとしましょう。二行目の大きさは 20 バイトとします。文字列が増加して 30 バイトに変更しようとします。ですが 20 バイトより先にはもう三行目のデータがあります。テキスト・ファイルでこれを実現するには新たにテキスト・ファイルを作るしかありません。
図にします。
テキスト・ファイルの構造
各レコード長はレコードごとに異なります。レコードには一つのフィールドしか存在しません。ですからフィールドの大きさも不定です。テキスト・ファイルに於けるフィールドとは行の事です。行は文字列で構成され終わりは改行コードで終了します。
文字コードや改行コードは気を付けてください。文字コードが異なる環境で作成されたテキスト・ファイルなら別の環境ではテキスト・ファイルとして意味を成しません。また ASCII コード体系のテキスト・ファイルであっても改行コードの扱いが異なる場合もあります。 C 言語では '\n' と記述すればどの様な環境でも改行コードとして働きます。それはコンパイラーが環境に合わせた内部コードに変換するからです。
テキスト・ファイルとして開いたストリームに対しては、この改行コードの変換が必要であれば自動的に行われています。しかしバイナリ・ファイルとして開くとこの変換はありません。ですからテキスト・ファイルに対して単純な処理を施すのならテキスト・ファイルとして開くのが最善です。コンパイラーによってはテキスト・ファイル指定でもバイナリ・ファイルとして処理しているものもあるかも知れません。
場合によってはテキスト・ファイルをバイナリ・ファイルとして開く必要も出てきます。その時は特に改行コードの変換をきちんとプログラムする必要があります。環境によって改行コードの扱いはまちまちです。 MS-DOS や Windows では 0x0D, 0x0A ( 実質 "\r\n" ) と二バイト必要です。他の環境では 0x0D だけ、0x0A だけ等有ります。念の為に注記しますが '\n' はどんな環境でも一文字であり一バイトです。テキスト・ストリームから読み込む時には環境固有の文字(列)コードから '\n' へと変換し、書き込む場合には '\n' をその環境固有の制御コード(列)へ変換して出力します。
この様な違いから他の環境で作成されたテキスト・ファイルが自分の環境で読み込めると決め付けるのも良くありません.
テキスト・ファイルは「各レコード長が不定であり、順次アクセスが基本、そして文字コード特に改行コードには気を付ける」という事を押さえてください。