たのしい工学

プログラミングを学んで、モノをつくりたいひと、効率的に仕事をしたい人のための硬派なブログになりました

【コンピュータサイエンス 独学 C言語】ファイルとストリームの概念

   

ファイルスコープ

有効範囲は四種類あります。関数スコープ、ファイル・スコープ、ブロック・スコープ、そして関数プロトタイプ・スコープです。構造体型の宣言の中は一体どれに当てはまるでしょうか。

同じ構造体型変数の比較

単純に二つの構造体型変数の識別子による比較は出来ません。メンバー変数単位で一つずつ比較するしか手はありません。
次の構造体型を考えます。


struct
{
int  a;
char b;
} x, y;

タグの無い構造体型変数 x と y を宣言しています。当然二つは同じ型です。ならば ( x == y ) と記述したくなるかも知れませんが出来ません。メンバー変数単位で比較を行います。
if ( ( x.a == y.a ) && ( x.b == y.b ) ) といった具合です。
構造体型のメンバー数が増えると比較するだけでも大変ですが他に方法がありません。

こう思われるかも知れません。

「構造体型変数の記憶領域を 1 バイトずつ比較して、同じなら二つの構造体型変数は同じといえるのではないだろうか」

素晴らしい疑問です。しかしパディングの事を考慮しなくてはいけません。パディングに用いられる値は不定ですしその位置も量も不明です。なので残念ながらその方法でも不可能です。

ファイル操作の基礎

ファイルの操作手順

1.ファイルをひらく
2.ファイルの読み書き
3.ファイルをとじる

ファイルを開く事をファイルをオープンするとも呼びます。ファイルを閉じる事をファイルをクローズするとも呼びます。
ファイルを開かなければファイルの読み書きは行えません。またファイルを閉じないとファイルの読み書きが正常に終了しません。ですからこの手順は絶対守らなければなりません。
ファイルを扱うのに必要な知識が他にもあるので順に説明します。

ファイル、レコード、フィールド

データを読み書きする単位をレコードと呼びます。レコードは意味付けされたフィールドを持ちます。構造体で考えると分かり易いでしょう。レコードは構造体型変数、フィールドはそのメンバー変数と思ってください。そしてレコードの集合体がファイルです。

テキスト・ファイルとバイナリ・ファイル

テキスト・ファイルとは文字コードと一部の制御コードで構成されたファイルの事です。例えばプログラム・ソースはテキスト・ファイルです。テキスト・ファイルは行単位で構成され行末は改行コードで終了します。
バイナリ・ファイルは全てのデータを扱います。 char 型(1byte)が 8 ビット環境にてその値を表現すればファイルのデータは 0x00 から 0xFF までの値の集合となります。広義ではテキスト・ファイルもバイナリ・ファイルですが、その様な意味でバイナリ・ファイルとの語句を用いる事は稀です。

ストリームの概念

stdin はマクロ定義にて (&_streams[0]) となっていますから、その式の型は配列 _strems の要素 0 へのポインターです。 _streams はその直前で外部変数の宣言があり、 FILE 型の配列である事が分かります。即ち stdin は FILE 型一次元配列の要素 0 へのポインターです。
これまで扱っていた標準入力、出力、エラー出力はストリームであり、且つテキスト形式を扱うテキスト・ストリームです。標準入力は主にキーボードでありファイルではなくキー入力装置です。これは一体どういう事なのでしょうか。

C 言語では移植性と利便性を高める為にストリームという概念を導入しています。 FILE 型を用いる関数はファイル操作を行う関数と表現するのは正しくありません。正確にはストリーム操作を行う関数です。
ストリームは謂わば仲介役で、本当の意味でのファイルであればそのファイルに対してアクセスし、キーボードなどの装置であればそれらに対してアクセスを行います。それらをストリームという共通手段でプログラムする環境を私達に提供してくれます。
プログラムする側はアクセス先がファイルであるか他の装置であるかは気にする必要は無く、ただストリームに対して操作すれば装置に対して適切に処理が行われる様になっています。しかし最初は、ストリームは対象がどの様なファイルや装置であるかを知りません。知っているのは使用者である私達です。それらをストリームに知らせると以後そのファイルや装置はストリームを介して操作出来る様になります。これがストリームとファイルを繋げる操作であり「ストリームを開く」事です。逆に繋がりを解くのが「ストリームを閉じる」事です。
stdin は &_streams[0] を示し、それは FILE 型へのポインターです。プログラム開始と同時に自動的に stdin を開き、ストリームと標準入力とを結び付けた結果としての FILE 型へのポインターがその配列に格納されています。またプログラム終了時には自動的に stdin を閉じ、ストリームと標準入力との結び付きを解きます。それは stdout や stderr も同様です。つまり標準入出力やエラー出力に対し実際にはストリームを開く処理や閉じる処理も行われていますが、それはコンパイラーが自動的にその様なプログラムを組み込んでいるからです。
外部記憶装置に存在するファイルに対しても、ストリームとファイルとを結び付けて初めてそのファイルに対してアクセスが可能になります。
ファイルを読み書きするのにその都度ファイルを開いたり閉じたりするのは手間だ、と思うのは誤りです。ストリームが存在しなければ処理装置ごとにそれに適した異なるプログラムを組む必要があり、その方が遥かに手間が掛かります。私達は装置が何であるかを気にせず、ただ「ストリームを開く」だけでアクセスが可能になります。最後に「ストリームを閉じる」事は忘れないでください。

ストリームを介してファイルにアクセスする手法を高水準入出力と呼びます。ストリームを介さず直接ファイルにアクセスする手法もあり、それを低水準入出力と呼びます。
低水準入出力はコンピューターの環境に依存した処理が必要になるので規格では定めていません。使用するとそれはコンパイラー依存であり環境依存です。
高水準入出力に於いて「ファイルを開く」を正しく表現すると「ストリームを開く」ですし、「ファイルを閉じる」とは「ストリームを閉じる」であり、「ファイルを読み書きする」とは「ストリームを読み書きする」となります
単にファイルと表現すると、実際のファイルを指す場合やストリームを指す場合があります。

同時に開けるストリームの数

stdin 、stdout 、 stderr はプログラム開始と同時に自動的に開かれます。プログラム起動と同時に三つのストリームを同時に開いている事になります。同時に開けるストリーム数の上限はあり、それは stdio.h に定義されている FOPEN_MAX にて知る事が出来ます。この値には先の三つのストリームも含めて考えます。つまり FOPEN_MAX より 3 減算した値が任意に開く事が可能なファイル総数となります。
この上限はコンパイラーの上限であってシステムの上限ではありません。システムの上限はもっと少なくなる可能性もあります。必要な時だけストリームを開き不要になったらすぐ閉じる様にしましょう。 malloc と同じです。

ストリームを開いた直後の読み書き位置

ストリームを開くとストリームを読み書きする位置は先頭になります。但しモードに "a" が含まれる場合、即ち追加モードの時には基準がストリームの先頭になるかストリーム末尾になるかは処理系定義です。
ストリームを読み書きするとこの位置が更新されます。書き込みの時は書き込んだ容量だけ進み、読み込みの場合は読込んだ容量だけ進みます。読み書きに失敗するとこの位置は不定になります。
ファイルを読み書きする位置は任意の場所に変更可能です。詳しくは別項で説明します。
ファイルを読み書きする位置をファイル・ポインターと称する事もあります。型としてのポインターと紛らわしいので本書ではこの呼称を使用しません。

文字列比較関数

文字列比較関数は標準で用意されています


ヘッダー・ファイル   string.h
プロトタイプ  int strcmp( const char * s1, const char * s2 );

使い方は簡単です。 s1 と s2 に文字列の先頭アドレスを指定します。 s1 の指し示す先の文字列が s2 の指し示す先の文字列より大きければ 0 より大きい値が、等しければ 0 が、小さければ 0 未満の値が戻ってきます。この関数は他の標準関数同様文字列が '\0' で終わっている事を前提としています。エラー判定は出来ません。

 - コンピュータサイエンス