第17章 ファイル入出力

ファイル入出力関数はキーボードや画面ではなく、ファイルに対して入出力を行います。ファイルは画面出力とは異なり出力結果の保存が可能ですので、覚えておくと何かと重宝します。

尚、ファイル入出力関数を使用するには、#include <stdio.h> する必要があります。

1. ファイル操作手順

ファイル入出力は通常、以下の手順で行います。

  • ファイルポインタとはFILE型へのポインタです。
  • FILE型では入出力の現在位置(ファイル位置指示子)、ファイルの終端に達したかの情報(ファイル終了指示子)、エラー情報(エラー指示子)、関連するバッファへのポインタなどのファイルの入出力を行う上での必要不可欠な情報を管理しています。
  • ファイル入出力を行う際には必ず、このファイルポインタを fopen関数によって取得しなければなりません。 また、入出力操作の完了とともに必ず fclose関数でファイルクローズしなければなりません。
// ファイル入出力例
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE *fp;    // ファイルポインタの宣言
    char s[256];
    
    // ファイルのオープン
    // ここで、ファイルポインタを取得する
    if ((fp = fopen("sample.txt", "r")) == NULL) {
        printf("file open error!!\n");
        exit(EXIT_FAILURE);    // エラーの場合は通常、異常終了する
    }
    
    // ファイルの読み(書き)
    // ここではfgets関数により1行単位で読み出し
    while (fgets(s, sizeof(s), fp) != NULL) {
        // 読み込んだ文字列を画面に表示して確認
        printf("%s", s);
    }
    fclose(fp);    // ファイルのクローズ
    
    return 0;
}

【実行結果例】
(sample.txt の内容を表示する)

ファイル入出力関数では、ファイルのアクセス位置は、fgets()などの読み書き関数を発行すると自動的に更新されます。通常のポインタのようにプログラム上で更新する必要はありません。

2. ファイル入出力関数

ファイル入出力に用いられる主な関数をまとめます。

(1) fopen関数

書式

FILE *fopen(const char *filename, const char *mode);

機能

  • ファイル名(filename)で示されるファイルを、指定モード(mode)でオープンする。
  • ファイルが正しくオープンされると、ファイルポインタが返される。
  • エラーの時は NULLが返される。

読み・書き・追加モード

モード動作ファイルがあるときファイルがないとき
"r"読み出し専用正常エラー(NULL返却)
"w"書き込み専用サイズを 0 にする(上書き)新規作成
"a"追加書き込み専用最後に追加する新規作成
"r+"読み込みと書き込み正常エラー(NULL返却)
"w+"書き込みと読み込みサイズを 0 にする(上書き)新規作成
"a+"読み込みと追加書き込み最後に追加する新規作成
  • “r”、”w”、”a” はそれぞれ「読み」、「書き」、「追加」の専用モードです。つまり、”r”モードで fopen したファイルに書き込み動作を行うことは出来ません。
  • “r+” と “w+” は一見同じ「読み書き」可能に見えますが、先に来るモードが主となるため、ファイルの有無で動作が異なります。
  • “a+” の書き込みは「追加書き込み」しか出来ません。

テキスト・バイナリモード

Linux(UNIX系)環境では、ファイルを開く際のモードの間に 動作上の違いはありません。どちらも同じくバイト単位で開かれます。しかし、Windows環境では以下のように違いがあります

バイナリモードWindows環境での動作
(省略時)

テキストモード

  • 出力時 : 改行文字 ‘\n’(0x0a) ⇒ ‘\r’ / ‘\n’(0x0d / 0x0a)に変換
  • 入力時 : ‘\r’ / ‘\n’(0x0d / 0x0a) ⇒ 改行文字 ‘\n’(0x0a)に変換
  • 0x1a を EOF(ファイルエンドとする)
"b"

バイナリモード("wb"、"rb" のように指定)

  • 上記の変換を行わない
  • 0x1a を EOF扱いしない
  • Windows系の OS では、テキストファイルの改行は ‘\r’(0x0d:復帰)と ‘\n’(0x0a:改行)の2つの コードで表されます。そのため、テキストモードで改行コード ‘\n’ をファイルに書き込もうとすると、’\r’ ‘\n’ の2文字に変換して書き込みます。逆に読み込みでファイルに ‘\r’ ‘\n’ の改行コードがあれば ‘\n’ の 1文字に変換します。
  • テキストファイル終端の 0x1a を EOF(ファイルエンド)として扱います。しかし、バイナリファイルには「0x1a」、「0x0d」、「0x0a」はデータとして存在する可能性があり、それをテキストモードのように変換されては困ってしまいます。そのため、Windows系のC言語ではテキストモードとバイナリモードを区別します。

使用例

FILE *fp;

if ((fp = fopen("sample.txt", "r")) == NULL) {
    /* エラー処理 */
}

(2) fclose関数

書式

int fclose(FILE *fp);

機能

  • fopen でオープンされたファイルポインタ(fp)で示されるファイルをクローズする。
  • エラーの時は、EOFが返される。

使用例

FILE *fp;
     :
fclose(fp);

(3) fgets関数

書式

char *fgets(char *s, int n, FILE *fp);

機能

  • ファイルポインタ(fp)で指定したファイルからの1行入力し文字列格納先(s)に格納する。1行の「最大文字数」を引数(n)で指定する必要がある。 この文字数には ‘\0’ も含まれるので、実際に入力できる文字数は「最大文字数 – 1」となる。
  • 入力が終了したら NULL を返す。
  • 指定するファイルは読み込み可能なモードで fopen する必要がある。

使用例

FILE *fp;
char str[256];
     :
fgets(str, sizeof(str), fp);

(4) fputs関数

書式

int fputs(const char *s, FILE *fp);

機能

  • ファイルポインタ(fp)で指定したファイルへ出力文字列(s)を出力する。
  • 指定するファイルは 書き込み可能なモードで fopen する必要がある。

使用例

FILE *fp;
:
fputs("abcdef", fp);

(5) fgetc関数

書式

int fgetc(FILE *fp);

機能

  • ファイルポインタ(fp)で指定したファイルから 1文字入力する。
  • 入力が終了したら EOF を返す。
  • 指定するファイルは読み込み可能なモードで fopen する必要がある。

使用例

FILE *fp;
int c;
     :
c = fgetc(fp);

(6) fputc関数

書式

int fputc(int c, FILE *fp);

機能

  • ファイルポインタ(fp)で指定したファイルへの 1文字(c)を出力する。
  • 指定するファイルは 書き込み可能なモードで fopen する必要がある。

使用例

FILE *fp;
     :
fputc('a', fp);

(7) fscanf関数

書式

int fscanf(FILE *fp, const char *format, …);

機能

  • ファイルポインタ(fp)で指定したファイルから書式つきで入力する。scanf のファイル版。ファイルポインタを指定する以外は scanf と同じ。
  • 指定するファイルは 読み込み可能なモードで fopen する必要がある。

使用例

FILE *fp;
int dt;
     :
fscanf(fp, "%d", &dt);

(8) fprintf関数

書式

int fprintf(FILE *fp, const char *format, …);

機能

  • ファイルポインタ(fp)で指定したファイルへ書式つきで出力する。printf のファイル版。ファイルポインタを指定する以外は printf と同じ。
  • 指定するファイルは書き込み可能なモードで fopen する必要がある。

使用例

ILE *fp;
int dt = 10;
:
fprintf(fp, "%8d\n", dt);

(9) ファイルコピーのプログラム例

#include    <stdio.h>
#include    <stdlib.h>    // EXIT_FAILURE(異常終了)を使用するためにインクルード

int main(void)
{
    FILE *fin,*fout;
    char infile[40],outfile[40],s[256];

    printf("入力ファイル名=");
    scanf("%39s", infile);
    printf("出力ファイル名=");
    scanf("%39s", outfile);

    if((fin = fopen(infile,"r")) == NULL) {     // 入力ファイルオープン
        printf("入力ファイルがオープンできません\n");
        exit(EXIT_FAILURE);
    }
    if((fout = fopen(outfile,"w")) == NULL) {   // 出力ファイルオープン
        printf("出力ファイルがオープンできません\n");
        fclose(fin);   // 先にオープンしたファイルをクローズ
        exit(EXIT_FAILURE);
    }

    while(fgets(s,sizeof(s),fin)!=NULL) {   // 入力ファイルから読み込んだデータを
        fputs(s,fout);                      // 出力ファイルに書き込み 
    }

    fclose(fin);                            // 入力ファイルクローズ */
    fclose(fout);                           // 出力ファイルクローズ */

    return 0;
}

【実行結果例】
※入力ファイルで指定したファイルを出力ファイルで指定したファイルにコピーする

〇 演習問題

問1

(9) ファイルコピーのプログラム例 のプログラムを fgetc()関数と fputc()関数を用いて書き換えなさい。
(注)fgetc()関数は入力が終了したら NULL ではなく EOF を返します。

問2

次の手順でキーボードから入力したデータをファイルに書き込むプログラムを作成しなさい。

  1. 追加モードで “car.txt” をオープンする。
  2. 無限ループで以下の処理を行う。
    1. メーカー名を文字列で入力する。 メーカ名に “end” が入力されたら無限ループを終了する。
    2. 車種を文字列で入力する。
    3. 排気量を 10進数で入力する。
    4. これらのデータを 1. でオープンしたファイルに書き込む。
  3. 1. でオープンしたファイルを閉じる。

【実行結果例】
メーカー名を入力して下さい(終了条件:end) HONDA
車種を入力して下さい ACCORD
排気量を入力して下さい 1993
メーカー名を入力して下さい(終了条件:end) TOYOTA
車種を入力して下さい PRIUS
排気量を入力して下さい 1797
メーカー名を入力して下さい(終了条件:end) end

【確認例】
C:\work>type car.txt
メーカー名:HONDA 車種:ACCORD 排気量:1993
メーカー名:TOYOTA 車種:PRIUS 排気量:1797

  • 水色文字はキーボードからの入力です。
  • typeコマンドでテキストファイルの内容を表示することができます。

解答例

// 問1
#include    <stdio.h>
#include    <stdlib.h>

int main(void)
{
    FILE *fin,*fout;
    char infile[40],outfile[40];
    int c;

    printf("入力ファイル名=");
    scanf("%39s", infile);
    printf("出力ファイル名=");
    scanf("%39s", outfile);

    if((fin = fopen(infile,"r")) == NULL) {   // 入力ファイルオープン
        printf("入力ファイルがオープンできません\n");
        exit(EXIT_FAILURE);
    }
    if((fout = fopen(outfile,"w")) == NULL) { // 出力ファイルオープン
        printf("出力ファイルがオープンできません\n");
        fclose(fin);   // 先にオープンしたファイルをクローズ
        exit(EXIT_FAILURE);
    }

    while((c = fgetc(fin)) != EOF) {          // 入力ファイルから読み込んだデータを
        fputc(c, fout);                       // 出力ファイルに書き込み
    }

    fclose(fin);                             // 入力ファイルクローズ */
    fclose(fout);                            // 出力ファイルクローズ */

    return 0;
}
// 問2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    FILE *fw;
    char maker[20], name[20];
    int cc;

    if((fw = fopen("car.txt", "a")) == NULL) {
        printf("ファイルがオープンできません。\n");
        exit(EXIT_FAILURE);
    }

    while(1) {
        printf("メーカー名を入力して下さい(終了条件:end) ");
        scanf("%19s", maker);
        if(strcmp(maker, "end") == 0)
            break;
        printf("車種を入力して下さい ");
        scanf("%19s", name);
        printf("排気量を入力して下さい ");
        scanf("%d", &cc);

        fprintf(fw, "メーカー名:%s ", maker);
        fprintf(fw, "車種:%s ", name);
        fprintf(fw, "排気量:%d\n", cc);
    }

    fclose(fw);

    return 0;
}

コメント