第15章 ファイル操作

1 ファイル処理の概要

教科書P226〜227

今まで、データの入力は標準入力装置(キーボード)から行い、出力は標準出力装置(ディスプレイ)へしていた。
では、データの入出力をファイル(テキスト・ファイル)で行うにはどうすればよいだろうか?

例えばscanfは書式付き標準入力関数、printfは書式付き標準出力関数であるため、基本的にはファイルからデータを読み込んだり、ファイルに出力することはできない(実行時にリダイレクションを使えば可能)。
C言語には、ファイルを操作する様々な関数が用意されているため、それらを利用してファイルへの入出力を行う。

2 ファイルのオープンとクローズ

教科書P228〜229

ファイルを利用するには、プログラム上でオープンとクローズを行わなければならない。サンプルは次のとおり。

ファイルオープン・クローズのサンプル

#include <stdio.h>
#include <stdlib.h>

void main(void)
{
    char filename[] = "data.txt"; /* データファイル名 */
    FILE *fp;

    /* ファイルを開く */
    if ((fp = fopen(filename, "r")) == NULL)
    {
        printf("ファイルが存在しません。プログラムを終了します。\n");
        exit(1);
    }

    /* データ処理 */
    ・
    ・
    ・

    /* ファイルを閉じる */
    fclose(fp);

}

プログラム解説

FILE *fp;
C言語でファイルを扱う場合、プログラム上で使用するファイルにはファイル・ポインタを割り当てる必要がある。複数のファイルを同時に扱う場合、同じ数のファイル・ポインタを宣言する。
FILE構造体はstdio.hで定義されているため、プログラムには必ずstdio.hをインクルードしなければならない。
fp = fopen(filename, "r")
FILE構造体で宣言したファイル・ポインタ「fp」に対して、ファイルをオープンする。fopen関数は2つの引数を持ち、1つ目の引数はファイル名(文字列)を、2つ目の引数にはオープン・モードを指定する。
オープンモードには次の種類がある。
モード意味
”r” リード(読み込み)・モード
ファイルはすでに存在していなければならない。
”w” ライト(書き込み)・モード
ファイルがなければ作成され、あれば中身が空になる。
”a” アペンド(追加書き込み)・モード
ファイルがなければ作成され、あればファイルの現在位置をファイル・エンドにセットする。
fopen関数は、ファイルのオープンに成功すると、ファイル・ポインタをオープンしたファイルの先頭(アペンド・モードの場合はファイル・エンド)にセットし、失敗するとNULLを返す。サンプルでは、NULLが返ってきたらメッセージを出してプログラムを終了するようにしてある。(exit関数を使うにはstdlib.hをインクルードしなければならない)
fclose(fp);
オープンしたファイルをクローズする。プログラム終了時、またはファイルが必要なくなったときに必ずクローズしなければならない(実際はリード・モードでオープンしたファイルはクローズしなくても実害はないが、開いたファイルはすべてクローズするようにプログラムするべきである。)。

ファイルの場所について

ファイル名を指定する際、パスを指定しなかった場合はプログラムを実行した場所(exeファイルがある場所)からファイルを探す。
Microsoft Visual C++の場合、プロジェクトから実行すると、プロジェクト・フォルダの中を探すが、ビルドによって作成されたexeファイルをダブルクリックして実行すると、exeファイルがあるフォルダ内を探す。exeファイルを配布する場合はデータ・ファイルの配置に注意する必要がある。

ファイル名にパスを指定する場合、例えばC:\data\data.txtを読み込む場合、プログラム上で次のように指定しなければならない。(Windows系の場合のみ)

char filename = "c:\\data\\data.txt"

※「¥」はエスケープ文字を表すための記号なので、「¥」という文字を使いたい場合は「¥¥」と指定しなければならないことに注意!!

3 1文字単位のファイル入出力

教科書P230〜231

ファイルのオープンに成功すると、ファイル・ポインタを用いてファイルからデータを入力したり、ファイルへ出力したりできるようになる。ここでは、1文字単位でデータを入出力する関数を紹介する。

getc / fgetc関数

《機能》

getchar関数のファイル・ポインタ版。引数に指定したファイル・ポインタから1文字(8ビット)読み込み、ファイル・ポインタを1文字(8ビット)移動させる。
ファイル・ポインタがファイルの一番最後(ファイル・エンド)までくると、EOF(−1)を返す。

《使用例:ファイルから1文字ずつ読み込み、CRTに縦に出力する》

#include <stdio.h>
#include <stdlib.h>

void main(void)
{
    char filename[] = "data.txt";
    FILE *fp;
    int c;

    /* ファイル・オープン */
    if ((fp = fopen(filename, "r")) == NULL)
    {
        printf("ファイル「%s」のオープンに失敗しました。プログラムを終了します。\n", filename);
        exit(1);
    }

    /* データ処理 */
    while ((c = getc(fp)) != EOF)
    {
        printf("%c\n", c);
    }
    
    /* ファイル・クローズ */
    fclose(fp);

}

《プログラム解説》

int c;
getcで読み込んだデータ(8ビット)を格納する変数の宣言。char型ではないことに注意。
fp = fopen(filename, "r")
ファイルからデータを読み込むため、リード・モードで開く。
(c = getc(fp)) != EOF
ファイル・ポインタから1文字(8ビット)分読み込み、int型変数cに格納する。戻り値がEOFでない間、while文を繰り返す。

※getc関数は改行文字も1文字として入力してくる。

putc / fputc関数

《機能》

putchar関数のファイル・ポインタ版。引数に指定したファイル・ポインタへ1文字(8ビット)出力し、ファイル・ポインタを1文字(8ビット)移動させる。

《使用例:文字列を1文字ずつファイルに出力する》

#include <stdio.h>
#include <stdlib.h>

void main(void)
{
    char filename[] = "out.txt";
    FILE *fp;
    int i;
    char data[] = "putc test-pro";

    /* ファイル・オープン */
    if ((fp = fopen(filename, "w")) == NULL)
    {
        printf("ファイル「%s」のオープンに失敗しました。プログラムを終了します。\n", filename);
        exit(1);
    }

    /* データ処理 */
    for (i=0 ; data[i]!='\0' ; i++)
    {
        putc(data[i], fp);
    }
    
    /* ファイル・クローズ */
    fclose(fp);

}

《プログラム解説》

fp = fopen(filename, "w")
ファイルへ出力するため、ライト・モードで開く。
putc(data[i], fp);
ファイル・ポインタの位置に1文字(8ビット)出力し、ファイル・ポインタを1文字(8ビット)ずらす。

練習問題

ファイルから1文字ずつ読み込んで、別のファイルに1文字ずつ出力するプログラムを作成しなさい。(出力したファイルは、読み込んだファイルとまったく同じ内容になる)
なお、読み込むデータファイルは自分で作成すること。

4 1行単位のファイル入出力

教科書P234〜237

getcやputc関数のように1文字ずつではなく、1行ずつ入出力を行う関数を紹介する。

fgets関数

《機能》

gets関数のファイル・ポインタ版。ファイル・ポインタから1行(\nまで)読み込んで文字配列に格納し、ファイル・ポインタを次の行の先頭へ移動させる。
ファイル・ポインタがファイルの一番最後(ファイル・エンド)までくる、または読み込みエラーが発生すると、NULL(0)を返す。

《使用例:ファイルから1行ずつ読み込み、CRTに出力する》

#include <stdio.h>
#include <stdlib.h>

#define MAX_STRINGS 80

void main(void)
{
    char filename[] = "data.txt";
    FILE *fp;
    char data[MAX_STRINGS];

    /* ファイル・オープン */
    if ((fp = fopen(filename, "r")) == NULL)
    {
        printf("ファイル「%s」のオープンに失敗しました。プログラムを終了します。\n", filename);
        exit(1);
    }

    /* データ処理 */
    while (fgets(data, MAX_STRINGS, fp) != NULL)
    {
        printf("%s", data);
    }
    
    /* ファイル・クローズ */
    fclose(fp);

}

《プログラム解説》

#define MAX_STRINGS 80
fgets関数は、2つ目の引数で「読み込む最大文字数」を指定しなければならないため、80文字にした。この場合、読み込むデータの長さは改行文字を含めて80文字以内にしなければならない。
char data[MAX_STRINGS];
getsで読み込んだデータ(改行文字までの任意の長さを持つ文字列)を格納する変数の宣言。
fgets(data, MAX_STRINGS, fp) != NULL
ファイル・ポインタから1行(改行文字まで)読み込み、文字配列dataに格納する。戻り値がNULLでない間、while文を繰り返す。

※MS−DOS系OSの場合、1行の終わりはCR(復帰)・LF(改行)の2文字で行っているが、gets関数はCR・LFの2文字を¥nの1文字に変換し、文字列の終わりに¥0を付与してから、文字配列に格納する。

fputs関数

《機能》

puts関数のファイル・ポインタ版。引数に指定したファイル・ポインタへ1行出力し、ファイル・ポインタを次の行へ移動させる。

《使用例:文字列をファイルに出力する》

#include <stdio.h>
#include <stdlib.h>

void main(void)
{
    char filename[] = "out.txt";
    FILE *fp;
    char data[] = "puts test-pro";

    /* ファイル・オープン */
    if ((fp = fopen(filename, "w")) == NULL)
    {
        printf("ファイル「%s」のオープンに失敗しました。プログラムを終了します。\n", filename);
        exit(1);
    }

    /* データ処理 */
    fputs(data, fp);
    
    /* ファイル・クローズ */
    fclose(fp);

}

《プログラム解説》

fp = fopen(filename, "w")
ファイルへ出力するため、ライト・モードで開く。
puts(data, fp);
ファイル・ポインタの位置に文字配列を出力し、ファイル・ポインタを次の行へ移動させる。¥0はCR・LFに変換してから出力する。

練習問題

ファイルから1行ずつ読み込んで、別のファイルに1行ずつ出力するプログラムを作成しなさい。(出力したファイルは、読み込んだファイルとまったく同じ内容になる)

5 書式付きファイル入出力

教科書P238〜239

標準入出力関数「scanf」「printf」のファイル・ポインタ版である「fscanf」「fprintf」関数を紹介する。これらを利用することにより、自分の好きなフォーマット(書式)でデータの入出力ができる。

fscanf関数

《機能》

scanf関数のファイル・ポインタ版。ファイル・ポインタから指定した書式に従って1行(\nまで)読み込み、書式に対応する各変数にデータを格納する。1行読み込んだら、ファイル・ポインタを次の行の先頭へ移動させる。
ファイル・ポインタがファイルの一番最後(ファイル・エンド)までくる、または読み込みエラーが発生すると、EOF(−1)を返す。

《使用例:ファイルから1行ずつ読み込み、CRTに出力する》

#include <stdio.h>
#include <stdlib.h>

void main(void)
{
    char filename[] = "data.txt";
    FILE *fp;
    int number;
    char name[20];

    /* ファイル・オープン */
    if ((fp = fopen(filename, "r")) == NULL)
    {
        printf("ファイル「%s」のオープンに失敗しました。プログラムを終了します。\n", filename);
        exit(1);
    }

    /* データ処理 */
    while (fscanf(fp, "%d,%s", &number, name) != EOF)
    {
        printf("番号:%d 氏名:%s\n", number, name);
    }
    
    /* ファイル・クローズ */
    fclose(fp);

}

《入力データ例》

1,Arai
2,Ando
3,Ohishi
4,Komine
5,Sakata

※1行のデータ内にカンマ区切りで番号と名前が登録されている。

《プログラム解説》

int number;
char name[20];
fscanf関数で読み込んだデータを格納する変数の宣言。
fscanf(fp, "%d,%s", &qmp;number, name) != EOF
ファイル・ポインタから指定した書式で1行読み込み、変数に格納する(今回はカンマ区切りで1つ目は整数、2つ目は文字列)。戻り値がEOFでない間、while文を繰り返す。

fprintf関数

《機能》

printf関数のファイル・ポインタ版。引数に指定したファイル・ポインタへ指定した書式で1行出力し、ファイル・ポインタを次の行へ移動させる。

《使用例:書式指定してファイルに出力する》

#include <stdio.h>
#include <stdlib.h>

void main(void)
{
    char filename[] = "out.txt";
    FILE *fp;
    int number = 15;
    char data[] = "puts test-pro";

    /* ファイル・オープン */
    if ((fp = fopen(filename, "w")) == NULL)
    {
        printf("ファイル「%s」のオープンに失敗しました。プログラムを終了します。\n", filename);
        exit(1);
    }

    /* データ処理 */
    fprintf(fp, "番号:%d データ:%s\n", number, data);
    
    /* ファイル・クローズ */
    fclose(fp);

}

《プログラム解説》

fprintf(fp, "番号:%d データ:%s\n", number, data);
ファイル・ポインタの位置に指定した書式でデータを出力し、ファイル・ポインタを次の行へ移動させる。

練習問題

ファイルから1行ずつ読み込んで、別のファイルに1行ずつ出力するプログラムを作成しなさい。
なお、入力データは1行に2つ以上の任意のデータを任意の書式で作成し、出力データは「項目名1:値 項目名2:値」という書式で出力すること。

6 アペンド・モードの使用例

教科書P229補足

ファイル・オープン時、モードに”a”を指定すると、ファイルへの追加書き込みになる。例えば次のプログラムを作成し、実行すると、実行するたびに出力・ファイルにデータが増えていく。

#include <stdio.h>
#include <stdlib.h>

void main(void)
{
    FILE *fp;
    
    /* ファイルをアペンド・モードで開く */
    if ((fp = fopen("out.txt", "a")) == NULL)
    {
        printf("ファイルのオープンに失敗しました。プログラムを終了します。\n");
        exit(1);
    }
    
    /* ファイルへ1行出力 */
    fputs("プログラムを実行するたびに行数が増えるぞぉ\n", fp);
    
    /* ファイルのクローズ */
    fclose(fp);
    
}

使い道がありそうでないような気が・・・


[ TOP ]