35章 テキストの読み書きその1

http://www.geocities.jp/ky_webid/c/035.html

ここからが本番ですね。実際に読み書きを行いたいです。

一文字読み込むfgetc関数

一文字書き出すfputc関数

ファイルの終端を調べれるfeof関数

エラーの発生を検知できるferror関数

複数文字読み込むfgets関数

複数文字書き出すfputs関数

わかりやすいですね。

今まで使ってきたfgets関数は三つ目の引数をstdinからファイルポインタに変えるだけで同じように使えます。便利ですね。

問題1

fgetc関数とfputc関数を使って、テキストファイルの内容を、別のテキストファイルにコピーするプログラムを作って下さい。

fgetsではなくfgetcを使うのがミソですね。

int main () {
    FILE *fp,*fp2;
    int c;
    
    fp = fopen("test.txt","r");
    if ( fp == NULL ) {
        return 1;
    }
    
    fp2 = fopen("test2.txt","w");
    if ( fp2 == NULL ) {
        return 1;
    }
    
    while (1) {
        c = fgetc(fp);
        if ( feof(fp) ) {
            // 終端まで行った場合
            break;
        }
        if ( c == EOF ) {
            // 何かエラーが起きた場合
            break;
        }
        if ( fputc(c,fp2) == EOF ) {
            // 何かエラーが起きた場合
            break;
        }
    }
    
    fclose(fp2);
    fclose(fp);
    return 0;
}

出来ました。

EOFと比較するよりもferrorの方がより厳格らしいのですが、一般的にはそこまでする必要がないとのことなので今回はEOFで実装してみました。

答え合わせ

解答例の方ではfgetcとfputcでEOFのチェックすら行ってないですね。この程度のプログラムではそこまでチェックする必要もないということでしょうか。

確かにここでエラーになって中断したところで書き込みは行われてるわけですから意味ないと言えば意味ないですね。

問題2

fgetc関数を利用して、自作のfgets関数を作って下さい。

自作のfgets関数!?これは久しぶりに難しそうです。

気合は入りますねー。うおおおお。

char *myfgets (char *str, int size, FILE *fp) {
    int c,i;
    char *p = str;
    
    for (i=0;i<size-1;++i) {
        c = fgetc(fp);
        if ( feof(fp) ) {
            break;
        }
        *str++ = c;
        if ( c == '\n' ) {
            break;
        }
    }
    
    *str = '\0';
    return p;
}

できました。結構キレイに書けたと思いますよ!

まずsize-1の値でループします。これは文字列の最後に\0が入るためです。

次にfgetcで一文字取得し、すでに空っぽの場合はfeofで偽になるのでループを抜けます。

空っぽでなければstrにcを代入し、インクリメントしてアドレスを進めておきます。

でもしcが改行コードだった場合、fgetsの仕様によりそこで処理の中断を行います。

最後にstrは最大格納文字列より一つ進んだアドレスになるはずなのでぴったりそこに\0を代入すれば完璧です。

答え合わせ

おお!殆ど同じ実装ですね。

ただ違うのはforの条件とferrorを使ってるところぐらいですかね。確かにこのようなコアな汎用プログラムの場合はferrorを使って厳密に処理した方が良いのかもしれません。

問題3

テキストファイルから空白を削除して、画面に表示するプログラムを作って下さい。

これって改行も含まれるんですかね?どっちか分からないのでとりあえずスペースの削除と定義して実装してみます。

int main () {
    FILE *fp;
    int c;
    
    fp = fopen("main.c","r");
    if ( fp == NULL ) {
        return 1;
    }
    
    while(1){
        c = fgetc(fp);
        if ( feof(fp) ) {
            break;
        }
        if ( c != ' ' ) {
            printf("%c",c);
        }
    }
    
    fclose(fp);
    return 0;
}

できました。

fgets関数の方を使おうとも思いましたが、結局一文字ずつチェックしないといけないのでfgetc関数の方を使わせていただきました。

答え合わせ

完璧ですね。まるでカンニングしたと思うくらい同じですw