38章 ランダムアクセス

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

ファイルを途中から読み込むための仕組みの話ですね。

ファイルの現在地を表すカレントポジションというものがあります。ファイルをオープンした直後はカレントポジションは先頭に位置します。

これを移動させることができるのがfseek関数というわけですね。

問題1

fseek関数とftell関数を利用して、ファイルのサイズを計算するプログラムを作って下さい。

fseekとftellを使ってファイルのサイズを計算するんですか。ちょっとパッと思いつかないですね。

色々試しながら実装してみます。

int main () {
    FILE *fp;
    long size;
    
    fp = fopen("main.c","r");
    if ( fp == NULL ) {
        return 1;
    }
    
    fseek(fp,0,SEEK_END);
    size = ftell(fp);
    fclose(fp);
    
    printf("main.cのサイズは %d です",size);
    
    return 0;
}

・・・と、思いきや全然複雑な話では無かったですね。

fseekで最後まで移動してftellでその位置を取得すればそれがサイズになりました。

答え合わせ

あってましたー!

問題2

double型の値を5つ書き込んだバイナリファイルを開き、3番目の値を画面に表示するプログラムを作って下さい。

これはつまり、前の章で習ったfwriteを使って書き込みを行ってからfseekせよということなのかな?

とりあえずやってみる。

int main () {
    FILE *fp;
    char *filename = "double.txt";
    double num[] = { 10.000, 20, 30, 40, 50 };
    double getnum;
    
    fp = fopen(filename,"wb");
    if ( fp == NULL ) {
        return 1;
    }
    
    // 書き込むデータ,データ一つのサイズ,データ数,ファイルポインタ
    fwrite(num,sizeof(double),5,fp);
    fclose(fp);
    
    fp = fopen(filename,"rb");
    if ( fp == NULL ) {
        return 1;
    }
    
    // ファイルポインタ,進めるサイズ,記号定数
    fseek(fp,sizeof(double)*3,SEEK_SET);
    
    // 受け取るデータ,データ一つのサイズ,データ数,ファイルポインタ
    fread(&getnum,sizeof(double),1,fp);
    
    fclose(fp);
    
    printf("%f",getnum);
    return 0;
}

なんとか出来ました。

fwriteとかfreadとか引数の意味をすぐ忘れてしまうのでコメントも書いてみました。

答え合わせ

ばっちしOKです。

問題3

適当なテキストファイルを開き、1文字おきに文字を表示するプログラムを作って下さい。ただし、全角文字が含まれていないものとします。

全角は含まないということはやはり全角文字の扱いが難しいってことでしょうね。いつかできるようになりたいです。

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

できました。特に難しいところはないですね。

答え合わせ

ああああああああああ!

問題文誤読です。一行おきにというのは取得するデータのことだったんですね。。。

ちょっと答えパッとしか見てないのでもう一度自分で実装しなおしてみます。

int main () {
    FILE *fp;
    char c;
    
    fp = fopen("99.txt","rb");
    if ( fp == NULL ) {
        return 1;
    }
    
    while(1){
        c = fgetc(fp);
        if ( feof(fp) ) {
            break;
        }
        printf("%c",c);
        fseek(fp,1,SEEK_CUR);
    }
    
    fclose(fp);
    return 0;
}

できました。うまくいってます。

解答例を見たところ

feof関数は2箇所に必要です。こうしないと、運悪くファイルの末尾(EOF)の部分をシークして通り抜けてしまうかも知れません。

と書いてありました。

どういうケースでそうなるのかがいまいちわかりませんでした。シークの値を色々変更してみたりしたものの通り抜けるパターンが分かりませんでした。

この辺はかなりクリティカルな感じがするのでまたちゃんと調べてみます。