C/C++ - 言語仕様編 第5回 〜ファイル入出力〜

文字列リテラルとエスケープシーケンス

"string"

のように、"と"で囲ったものが、文字列リテラルである。

文字列リテラル内にある '\' は、その次の文字を制御文字として認識する。 もっともよく使われるものが、改行文字を表す、'\n'である。

"string\n"

などとすれば、「string」という文字列の後に改行文字が付く文字列リテラルとなる。

いくつか代表的なエスケープシーケンスを書いてみる。

n :改行文字
t :水平タブ文字
0 :NULL文字
' :シングルクォーテーション文字
" :ダブルクォーテーション文字
\ :バックスラッシュ文字

文字列リテラルは、const char *型ポインタ変数で受け取ることができる。

FILE 構造体

バイナリファイルを取り扱う場合は、stdio.h ヘッダに宣言されている FILE 構造体を用いることとなる。 バイナリファイルの入出力は、基本的に stdio.h ヘッダ内で定義される、高水準ファイル入出力を行う 関数を用いることとなる。

FILE 構造体は、よほどの特殊な事情がない限り、必ずFILE *ポインタ変数として取り扱う。 また、これまた特殊な事情がない限り、FILE構造体のメンバ変数を気にする必要は全くない。

バイナリファイルオープン

動的変数を取得するときに、malloc()関数を用いたのと同じ感覚で、

FILE *fopen(const char *Filename, const char *Mode);

を用いる。

Filenameも、Modeもconst char *型変数であることからわかるように、文字列リテラルを受け取る。 Filenameはその名のとおり、ファイルの名前である。

"filename.dat"などとしておけば、ソースファイルがあるフォルダ内の、"filename.dat"という ファイルが対象となる。

ただし、ファイルオープンの場合には、さらにModeが必要となる。

ここでは、Modeとして指示可能な代表的な文字列リテラルを挙げる。

"rb" :読み込みモードで開く。
    第1引数のファイルが必ず存在しなくてはならない
"wb" :新規書き込みモードで開く。
    第1引数のファイルの存在性に問わず、
    オープンした直後は内容がクリアされる。
"ab" :追記書き込みモードで開く。
    第1引数のファイルが存在していれば、続けて書き込まれ、
    存在しない場合は、新規書き込みモードとなる。
"r+b":更新モードで開く。
    第1引数のファイルは必ず存在しなくてはならない
"w+b":新規更新モードで開く。
    第1引数の存在性を問わず、
    オープンした直後は内容がクリアされる。
"a+b":追記更新モードで開く。
    第1引数のファイルが存在していれば、続けて書き込まれ、
    存在しない場合は新規更新モードとなる。	

ファイルクローズ

動的変数の領域解放をするときに、free()関数を用いたのと同じ感覚で、

int fclose(FILE *fp);

を用いる。

バイナリファイル出力関数

書き込みモード、または更新モードで開いたファイルポインタ fp を引数とする

size_t fwrite(
	void *buffer, size_t size, size_t n, FILE *fp
);

を用いる。

T 型変数の書き込み

第1引数が汎用変数であるvoid *型ポインタ変数であることからわかるように、 第1引数には、変数のポインタを入れることとなる。

第2引数には、sizeof(T)が入り、第3引数は必ず1が入る。そして、第4引数に 書き込みモード、または更新モードでオープンしたファイルポインタを指定することで、 T型変数が出力される。

正しく出力されると、出力したデータの数、要するに1が返る。 型Tは、ポインタ型でない基本型や構造体など、ほとんどすべての型で問題なく使える。

T[N] 型配列の書き込み

T Array[N]; であるとき、配列を書き込むときは、第1引数が配列の先頭を表す参照子である、 Array、第2引数はsizeof(T)、第3引数はsizeof(Array)/sizeof(Array[0]) (=N) であり、 第4引数は書き込みモード、または、更新モードでオープンしたファイルポインタを指定する。

正しく出力されると、Nが返る。

T *型ポインタ変数の書き込み

T *p; であるとき、第1引数はポインタ変数pであり、第2引数はsizeof(T)となる。 第4引数はファイルポインタで問題ないが、第3引数に入る値は、T [N]型配列の場合と異なり、 必ず何らかの方法で管理しなくてはならない。

なお、関数の引数内で初期化されるT [N]型配列も、ポインタ変数扱いとなるため、 第3引数に入るべき数値を別に管理する必要がある。

バイナリファイル入力関数

読み込みモード、または更新モードで開いたファイルポインタfpを引数とする

size_t fread(
	void *buffer, size_t size, size_t n, FILE *fp
);

を用いる。

T 型変数の読み込み

T型変数の書き込みの引数の指示と全く同じ指示で読み込みを行う。 第1引数には、変数のポインタをいれ、第2引数にはsizeof(T)、第3引数には必ず1が入り、 第4引数には読み込みモード、または更新モードで開かれたファイルポインタを指定する。

正しく読み込みができると1が返る。

書き込みのときそうであったように、ポインタ変数でないT型変数であれば、基本型でも構造体でも 基本的に全く問題なく使用することができる。

T [N]型配列の読み込み

T Array[N]; に対して、第1引数はArray、第2引数はsizeof(T)、第3引数はN、 第4引数は、読み込みモード、あるいは更新モードで開いたファイルポインタを指定する。

正しく読み込みができるとNが返る。

T *型ポインタ変数の読み込み

T *p; に対して、第1引数をp、第2引数をsizeof(T)、第4引数をファイルポインタとすれば、 読み込むことが可能となる。ここで、第3引数の値 n*sizeof(T)の長さの、 なんらかの記憶領域が確保されていなければならない。

ランダムアクセス

fwrite()関数やfread()関数はシーケンシャルアクセスをする。要するに、 関数を実行すると、ファイルポインタは、その書き込み・読み込み位置である、 ファイル位置指示子をサイズ分だけ自動的に進める。 したがって、通常は、書き込み・読み込み開始地点から順番に、1レコードずつアクセスすることになるが、 ここでは、任意の場所から書き込み・読み込みをする方法であるランダムアクセスの方法を述べる。

ファイル位置指示子の取得

long ftell(FILE *fp);

を使用すると、ファイルポインタfpのファイル位置指示子が返る。 ファイル位置指示子は、ファイルの先頭位置を0バイトとする絶対位置が返る。

ファイル位置指示子の移動

int fseek(FILE *fp, long offset, int whence);

は、ファイルポインタfpの ファイル位置指示子をwhenceで指定した位置からのoffset相対位置に指定する。

whenceには、定数としてファイルの先頭を表すSEEK_SET、現在位置であるSEEK_CUR、 終端位置であるSEEK_ENDの3つが定義される。

特に、ファイルポインタの先頭に戻る場合には、

void rewind(FILE *fp);

が用意されている。

ランダムアクセスにより移動できる範囲は、当然ではあるが、ファイルの先頭から終端までである。 ファイルの終端では、ファイル位置指示子として定数EOFが返る。

int feof(FILE *fp);

が0以外の値を返すと、ファイル位置指示子は、 ファイルの終端に位置し、これより後方へのランダムアクセスは不可能である。

書き込みモード・更新モードの場合、動的変数などの場合と異なり、 ファイル位置指示子がEOFである場合でも、fwrite()関数を用いてデータを書き込むことができる。 当然ではあるが、fread()関数などは使用することができない。また、fseek()関数やftell()関数が、 ファイル終端位置で呼び出されると、EOFが返る。

エンディアン

基本的に、データの最小書き込み単位は8ビット、1バイト単位である。 エンディアンとは、整数型など、4バイトを記録単位とするような場合にどのようにデータを格納するかを 表す方式である。

16進数0x1234ABCDに対して、1234ABCDと上位バイトから並べる方式をビッグエンディアン、 CDAB3412と下位バイトから並べる方式をリトルエンディアンと呼ぶ。

ビッグエンディアンは人間にとって直感的であり、リトルエンディアンはCPUにとって処理しやすい。 Windowsを使ってる限りにおいては、普通、リトルエンディアンで記録される。

基本的には、バイナリファイル入出力を考える限りにおいては、メモリの取り扱いと ほとんど直感的には同じ感覚で書き込みや読み込みを行うことができる。

メモリに記録する場合と決定的に異なる点は、書き込みや読み込みに、 メモリの場合に比べ、圧倒的に時間がかかる点である。

バイナリファイルは、メモリに記録する、つまり、変数に書き込まない限り、CPUで演算を行うことは出来ない。

次回、ポインタと配列に次ぐ、C言語最大の壁、文字列操作にメスを入れる。


文章作成 : yukki-ts (-+-twilight serenade-+- [stage])