ファイル入出力


ファイル入出力の基本

今までは、キーボードから入力し、画面に出力するスタイルでプログラムを 作成して来たが、これをファイル入出力で行うことも出来る。

#include <stdio.h>

int main(void)
{
    FILE* fp;
    double a, b;

    fp = fopen("testdata.txt", "r");

    fscanf(fp, "%lf", &a);
    fscanf(fp, "%lf", &b);
    printf("%f\n", (a+b)/2);

    fclose(fp);

    return 0;
}
filein.c

これは、testdata.txtというファイルから2つの数値を読み込んで、 その平均を表示するプログラムである。

FILE *fp;」は、変数fpを「FILE構造体へのポインタ」 と宣言している。FILE構造体はstdio.hの中で定義されているが、 この中身の詳細を知る必要は無い。ファイルを扱うにはこのように宣言する、 と覚えておけば十分である。このような変数のことを「ファイルポインタ」 と呼ぶ。

fopenfcloseは対で使われ、ファイルの使用開始と 使用終了を示す。fcloseは忘れがちなので注意すること。 fopenの第1引数には文字列でファイル名を指定する。 fopenの第2引数には、ファイルを読み込む場合は文字列で "r"と指定する(readの意)。

fscanfはファイル入力の場合にscanfの代わりに 用いるもので、先頭にファイルポインタを指定する以外はscanfと 同じ使い方である。

#include <stdio.h>

int main(void)
{
    FILE* fp;
    int c;

    fp = fopen("testdata2.txt", "r");

    while (1) {
        c = fgetc(fp);
        if (c == EOF) break;
        putchar(c);
    }

    fclose(fp);

    return 0;
}
filein2.c

この例は、testdata2.txtというファイルから 一文字ずつ読み込んで、それをそのまま一文字ずつ画面に出力する ものである。

fgetcは、ファイルポインタで指定されたファイルから一文字読み込む。 fscanf(fp, "%c", &c);と同じ。

putcharは、printf("%c", c);と同じ。

EOFは、読み込みがエラーになった場合に「エラーを示す文字」として 返されるものである。End Of Fileの略。「-1」と設定されているシステムが多い。 fgetcなどの文字を扱う関数は、(文字型なので)0〜255の 数値と、このエラーを示すための「-1」を扱う必要があり、そのため 返却される値はchar型やunsigned char型でなく int型になっており、注意する必要がある。fgetcの 返却した値は、int型の変数で扱わなければならない。

#include <stdio.h>

int main(void)
{
    FILE* fp;
    int i, s;

    fp = fopen("testdata3.txt", "w");

    s = 0;
    for (i=1; i<=100; i++) {
        s += i;
        fprintf(fp, "%d\n", s);
    }

    fclose(fp);

    return 0;
}
fileout.c

この例は、計算結果をtestdata3.txtというファイルに 書き出している。fopenの第2引数は、書き込み用なので 「"w"」になっている(writeの意)。printfの代わりに fprintfが使われている。

#include <stdio.h>

int main(void)
{
    FILE* fp;
    FILE* fp2;
    int c;

    fp = fopen("testdata4.txt", "r");
    fp2 = fopen("testdata5.txt", "w");

    while (1) {
        c = fgetc(fp);
        if (c == EOF) break;
        fputc(c, fp2);
    }

    fclose(fp);
    fclose(fp2);

    return 0;
}
fileinout.c

この例では、testdata4.txtの内容を一文字ずつ読み出して、 それをそのままtestdata5.txtに書き出している。 つまり単なるファイルコピーを行っている。

このように、複数のファイルを同時に扱うときは、複数の異なる名前の ファイルポインタを用いればよい。

なお、fopenの第2引数には、「"a"」もあり、 こちらは「"w"」と同様に書き込みを行う。 両者ははじめから同じ名前のファイルがあった場合の挙動が異なり、 「"w"」は古いファイルを消して一から書く、 「"a"」は古いファイルの中身を残したまま末尾に追加する(appendの意)。

標準入出力

システムには実ははじめからopenされている3つのファイルポインタ
stdin
stdout
stderr
がある。 stdin(標準入力)はキーボード入力、stdout(標準出力)と stderr(標準エラー出力)は画面に設定されている。 このように、画面やキーボードとファイルを同一視して 扱うようになっている。 実は、

putchar(c) fputc(c, stdout)
getchar()fgetc(stdin)
printf( ... )fprintf(stdout, ... )
scanf( ... )fscanf(stdin, ... )

この表のような関係になっており(左と右は同じ)、右側の ファイルポインタを使った関数が基本で、左側はそれをstdinstdoutに限った特別版になっている。

リダイレクトとstderr

(以下はOSの機能に関係が深い話)

stderrstdoutと同様、画面に出力する。 stdoutとの違いを説明するには、UNIX系OSに標準的に 使われているリダイレクト(redirect)という機能を知る必要がある。

例えば、dateというコマンドを実行すると画面に 現在時刻が表示されるが、それを画面に出さずにファイルに出すように 切替えることが出来る。

% date
Tue Jun  7 08:42:08 JST 2016
% date > date.txt
% cat date.txt
Tue Jun  7 08:42:14 JST 2016
%
このように、「> ファイル名」を付加することによって、 標準出力に出力されるデータをファイルに書き込ませることが出来る。

また、「< ファイル名」を付加することによって、 キーボード入力(標準入力)をファイルから行わせることも出来る。

で、標準出力と標準エラー出力の違いは、標準エラー出力はこの リダイレクト指示の影響を受けない点にある。 例えば、

#include <stdio.h>

int main(void)
{
    printf("This is stdout\n");
    fprintf(stdout, "This is stdout\n");
    fprintf(stderr, "This is stderr\n");

    return 0;
}
stderr.c

のようなプログラムを実行すると、

% ./stderr
This is stdout
This is stdout
This is stderr
% ./stderr > stderr.txt
This is stderr
% cat stderr.txt
This is stdout
This is stdout
%
のように、一見同じように画面に出ているように見えても、リダイレクトした ときの挙動がstdoutstderrで異なることが分かる。 stderr(標準エラー出力)は、その名前が示すように、 主にエラーメッセージを表示する目的で使われる。 すなわち、リダイレクトを行っている場合でも、エラーメッセージは 画面に表示されてユーザが気づくように、という配慮である。

ファイルオープン失敗

fopenに失敗すると「NULLポインタ」が返って来る。
fp = fopen(...);
if (fp == NULL) {
	error処理
}
のようにエラー処理しましょう。
ファイル操作