ほぷしぃ

納得C言語!

[第16回]ファイル入出力

ファイル入出力


ファイル入出力とは、今まで画面に入出力していたものをファイルに入出力するというものです。
普通のメモ帳やMicrosoft Wordなどのアプリケーションでファイル入出力ができるのと同じく、コンソール画面でもファイルの入出力が可能です。

A「ところで、ファイル入出力で何が出来るようになるの?」
B「今までのプログラムでは、終了するとそれまで入力したデータは消えてしまうよね」
A「実行するごとにいちいちデータを入力しなきゃいけなかったら面倒くさかったよ」
B「そうだね。ファイル入出力が出来るようになるとデータの保存や読み出しが出来るようになるんだ」
A「おお!!」

さて、それではファイル入出力の流れを見ていきましょう。


1.ファイル入出力の流れ

ファイル入出力は以下の手順を踏みます。

(1)ファイル構造体のポインタ作成
               
(2)ファイルオープン
               
(3)ファイルの読み書き
               
(4)ファイルクローズ

上から順を追って説明していきましょう。

(1)ファイル構造体のポインタを作成

ファイル入出力を行うためには、まずファイル構造体のポインタを作成する必要があります。
ポインタの作成方法は以下の通りです。

構造体ポインタ

この一文を書くだけで、ファイル構造体のポインタが作成されます(変数名は各自お好みで)。
そう言えば、ファイル構造体を作っていないのに宣言が可能なのでしょうか?
実は既にファイル構造体は出来上がっているのです。
ソースコードの最初におまじないとして書いている「#include <stdio.h>」を入れることによって自動的にファイル構造体が作られています。
興味のある人は、" C:\Program Files\Microsoft Visual Studio 8\VC\include\stdio.h"(インストール場所によって" C:\Program Files"の場所は違う)の59〜69行目を見てみよう。
59〜69行目に記述されているのが実はファイル構造体なのです。
「#include <stdio.h>」を入れることによって59〜69行目の構造体が使えるのかは次回で確認してください。

(2)ファイルオープン

ファイルを開くにはfopen()関数を使います。
fopen()関数の使い方は以下の通りです。

ファイルオープン

ファイル名は開きたいファイル名を記述します。
オープンモードはファイルをどのように開くかを決めます。
オープンモードの種類は以下の通りです。

モード動作ファイルがある場合ファイルがない場合
"r"読み出し専用変化なし失敗
"w"書き込み専用ファイル上書き新規作成
"a"追加書き込み専用ファイルの最後に追加新規作成
"r+"読み込みと書き込み変化なし失敗
"w+"書き込みと読み込みファイル上書き新規作成
"a+"読み込みと追加書き込みファイルの最後に追加新規作成

fopen()関数はファイルを開く時にファイルを正しく開くことが出来ると様々なファイル情報を持つFILE型のポインタ変数を返します。
このFILE型のポインタ変数の事をファイルポインタといいます。
正しく開くことが出来なかった場合は失敗とみなされNULLを返します。

(4)ファイルクローズ

ファイルの読み書きの前にファイルの閉じ方を説明します。
ファイルを閉じるにはfclose()関数を使います。
fclose()関数の使い方は以下の通りです。

ファイルクローズ

fopen()関数によって開いたファイルをfclose()関数で閉じます。
こうすることで開いたファイルを閉じることが出来ます。
ここまでで簡単なファイル操作が出来ますので、例題で確かめてみましょう。
今回はtest.datをいう名前のファイルを開いて閉じるというのをやってみます。

例題1 ファイルを開いて閉じる

#include <stdio.h>

int main(){
    //ファイル構造体へのポインタを宣言
    FILE *fp;
    char str[256];

    //ファイルを書き込みモードで開く
    fp = fopen("test.dat","w");
    
    //ファイルオープンに失敗した場合
    if(fp==NULL){
        //失敗と表示し終了
        printf("ファイルオープン失敗\n");
        return -1;
    }

    //ファイルを閉じる
    fclose(fp);
    return 0;
}

結果

確認のため、出力しました

ファイルを書き込みモード"w"で開き、ファイルが存在していなかったのでソースコードがあるところにtest.datというファイルが作成されています。
テキストエディタなどで開いて中身を見てもらえば分かるとおり、プログラムは開いて閉じるだけの処理なので中身はカラッポです。

(3)ファイルの読み書き

先ほど飛ばしたファイルの読み書きについて説明します。

fgets()関数

fgets()関数

fgets()関数はファイルポインタで指定したファイルから読み取る文字数だけ読み取り、文字配列に格納します。
読み取る文字数にはヌル文字も含まれるので読み取る文字数の最大値は指定した数値−1となります。
指定したファイルの途中で改行やファイルの終了を見つけた時は読み取りを終え、改行も配列に格納されます。
そして、最後に格納した配列の次の配列にヌル文字を入れて文字列の終了とします。

例題2 この例題のソースファイルを出力する

#include <stdio.h>

int main(){
    //ファイル構造体へのポインタを宣言
    FILE *fp;
    char str[1024];

    //01.cppを読み込みモードで開く
    //ファイル名は自分でつけたファイル名を書いてください
    fp = fopen("01.cpp","r");

    //ファイルオープンに失敗したとき
    if(fp==NULL){
        //失敗を表示し終了
        printf("ファイルオープン失敗\n");
        return -1;
    }

    //fgetsの戻り値がnullになるまで続ける
    //strにファイルからバイト取得し格納
    while((fgets(str,256,fp))!=NULL){
        //格納された文字を出力
        printf("%s",str);
    }

    //ファイルを閉じる
    fclose(fp);
    return 0;
}

結果

ソースをコンソール画面に表示

例題2の中身が出力されていますね。


fgetc()関数

fgetc()関数

fgetc()関数はファイルポインタで指定したファイルから1文字入力します。
fgetc()関数はgetchar()関数と使い方が似ているところがあります。

例題3 この例題のソースファイルを出力する

#include <stdio.h>

int main(){
    //ファイル構造体へのポインタを宣言
    FILE *fp;
    char str;

    //読み込みモードでファイルを開く
    fp = fopen("01.cpp","r");

    //ファイルが開けなかったとき
    if(fp==NULL){
        //失敗を表示して終了
        printf("ファイルオープンエラー\n");
        return -1;
    }

    //ファイルの終端(EOF)になるまで続ける
    //ファイルから一文字読込strに格納
    while((str = fgetc(fp))!=EOF){
        //strを出力
        printf("%c",str);
    }

    //ファイルを閉じる
    fclose(fp);
    return 0;
}

結果

ソースをコンソール画面に表示

こちらもファイルの中身が出力されましたね。
余談ですが、fgetsは指定されたバイト分(バッファ)を読み込み出力しています。
それに対してfgetcは1文字(1バイト)読んで出力しています。
なのでfgetcの方が見た目ゆっくり出力されているように見えるかもしれません。


fscanf()関数

fscanf()関数

fscanf()関数はファイルポインタで指定したファイルから文字や数値を入力します。
fscanf()関数はscanf()関数のファイル版にあたります。
(1)は指定文字列、(2)は入力データを格納する変数のアドレスを表します。


fputs()関数

fputs()関数

fputs()関数はファイルポインタに指定したファイルに文字配列に格納されている文字列を書き込みます。
fputs()関数はputs()関数のファイル版にあたります。

例題4 この例題のソースファイルを違うファイルへ出力する

#include <stdio.h>

int main(){
    //ファイル構造体へのポインタを宣言
    //読込みようファイルポインタ:lf
    //書き込み用ファイルポインタ:sf
    //をそれぞれ宣言する
    FILE *lf,*sf;
    char str[256];
    int c;

    //読み込みモードでファイルを開く
    lf = fopen("01.cpp","r");
    //エラー
    if(lf==NULL){
        printf("ファイルオープンエラー\n");
        return -1;
    }
    
    //書き込みモードでファイルを開く
    sf = fopen("test.txt","w");
    //エラー
    if(sf==NULL){
        printf("ファイルオープンエラー\n");
        return -1;
    }

    //fgetsがNULLになるまで繰り返す
    //fgets(str,256,lf)!=NULL
    //と同じです。このように短縮することも可能
    while(fgets(str,256,lf))
    {
        printf("%s", str);
        //strに格納されている文字列をsfに書き込む
        fputs(str,sf);
    }
    
    //sfクローズ
    fclose(sf);
    //lfクローズ
    fclose(lf);

    return 0;
}

結果

ソースをコンソール画面に表示

出力されました。ファイルにも書き込まれているでしょうか?

ちゃんと書き込まれていますね

ちゃんと書き込まれていますね。


fputc()関数

fputc()関数

fputc()関数はファイルポインタで指定したファイルに1文字を書き込みます。
fputc()関数はputchar()関数と使い方が似ているところがあります。

例題5 この例題のソースファイルを違うファイルへ出力する

#include <stdio.h>

int main(){
    //ファイル構造体へのポインタを宣言
    //読込み用、書き込み用それぞれ宣言する
    FILE *lf,*sf;
    int c;
    
    //読み込み、書込みそれぞれでオープン
    //短縮して書いていますが、
    //if(lf=fopen("01.cpp","r")==NULL){}
    //if(sf=fopen("test.txt","w")==NULL){}
    //の場合と同じです。
    if(!(lf=fopen("01.cpp","r")) || (!(sf=fopen("test.txt","w")))){
        printf("ファイルオープンエラー\n");
        return -1;
    }

    //ファイルの中身がEOFになるまで続ける
    //ファイルから一文字読み込みcへ格納
    while((c=fgetc(lf))!=EOF){
        //画面に出力
        putchar(c);
        //ファイルに出力
        fputc(c,sf);
    }

    //書込みクローズ
    fclose(sf);
    //読み込みクローズ
    fclose(lf);

    return 0;
}

結果

ソースをコンソール画面に表示

こちらもファイルに出力されているか確認してみましょう。

ちゃんと書き込まれていますね

はい、しっかり書き込みされていますね。


fprintf()関数

fprintf()関数

fprintf()関数はファイルポインタで指定したファイルに文字や数値を出力します。
fprintf()関数はprintf()関数のファイル版にあたります。
(1)は書式指定文字列、(2)は変数を表します。


2.エラー処理

fopen()関数を使ってファイルを開くことが出来ますが、必ずしもファイルを開くことができるとは限りません。
ファイルを開くことが出来ないままファイルを操作する処理を実行するのはとても危ないです。
そこで、エラーを起こした時はそのままプログラムを実行させるのではなく、エラー処理をさせ、プログラムを正しく動かします。
fopen()関数で正しくファイルを開くことが出来ない場合はNULLを返しますが、それを使うことでエラー処理を行うことが出来ます。
エラー処理の方法は以下の通りです。

エラー処理

"エラー処理"とコメントアウトしている部分に処理を記述すると、ファイルオープンに失敗した場合にエラー処理を実行することになります。
慣れてくればこのように書いても大丈夫です。

エラー処理

fopen()関数とifを1行にまとめました。
()のくくり方が若干複雑なので、書き方に慣れていない人は使わないほうが無難でしょう。
次に、エラー処理の内容について説明します。
エラー処理は基本的に「エラーしましたよ」というのを表示してからプログラムを強制終了させます。
表示する部分はprintf()関数を使って表示させます。
強制終了はreturn文を使っても構わないですが、自作関数内でreturn文を使うことになると、呼び出し元へ戻ってしまいます。
そこで使うのがexit()関数と呼ばれるものです。
exit()関数はプログラムを終了させる関数で、exit()関数を使うには「#include<stdlib.h>」を追加する必要があります。
exit()関数の使い方は以下のようになります。

exit()関数

ステータスはexitによる終了が正常終了なのか異常終了なのかを調べます。
ステータスの値は基本的に決められた数値はありませんが、正常終了が0、異常終了が1というのが暗黙の了解で決まっています。
さらに0はEXIT_SUCCESS、1はEXIT_FAILUREと書きかえることができます。
どちらを使うかはお好みで。


3.練習問題

(1)次の文章を入力し、ファイルに保存してください。
「end」を入力で終了します。

日本国民は、正当に選挙された国会における代表者を通じて行動し、われらとわれらの子孫のために、諸国民との協和による成果と、わが国全土にわたって自由のもたらす恵沢を確保し、政府の行為によって再び戦争の惨禍が起ることのないようにすることを決意し、ここに主権が国民に存することを宣言し、この憲法を確定する。そもそも国政は、国民の厳粛な信託によるものであって、その権威は国民に由来し、その権力は国民の代表者がこれを行使し、その福利は国民がこれを享受する。これは人類普遍の原理であり、この憲法は、かかる原理に基くものである。われらは、これに反する一切の憲法、法令及び詔勅を排除する。

(2)(1)で保存した文章を読み出してください。


[第15回]演習問題W ページのトップ 解答