C言語入門(50) 実際にプログラムを読んで理解を深めよう tail編(5)

Posted: 2013年07月25日

前回は、バイト単位での読み込み処理を見ました。
今回は、行単位での読み込み処理です。

read.c lines関数

プログラムプログラム:

/*
 * lines -- read lines to an offset from the end and display.
 *
 * This is the function that reads to a line offset from the end of the input,
 * storing the data in an array of buffers which is then displayed.  If the
 * rflag is set, the data is displayed in lines in reverse order, and this
 * routine has the usual nastiness of trying to find the newlines.  Otherwise,
 * it is displayed from the line closest to the beginning of the input to
 * the end.
 */
int
lines(FILE *fp, const char *fn, off_t off)
{
    struct {
        int blen;
        u_int len;
        char *l;
    } *llines;
    int ch, rc;
    char *p, *sp;
    int blen, cnt, recno, wrap;

    if ((llines = malloc(off * sizeof(*llines))) == NULL)
        err(1, "malloc");
    bzero(llines, off * sizeof(*llines));
    p = sp = NULL;
    blen = cnt = recno = wrap = 0;
    rc = 0;

    while ((ch = getc(fp)) != EOF) {
        if (++cnt > blen) {
            if ((sp = realloc(sp, blen += 1024)) == NULL)
                err(1, "realloc");
            p = sp + cnt - 1;
        }
        *p++ = ch;
        if (ch == 'n') {
            if ((int)llines[recno].blen < cnt) {
                llines[recno].blen = cnt + 256;
                if ((llines[recno].l = realloc(llines[recno].l,
                    llines[recno].blen)) == NULL)
                    err(1, "realloc");
            }
            bcopy(sp, llines[recno].l, llines[recno].len = cnt);
            cnt = 0;
            p = sp;
            if (++recno == off) {
                wrap = 1;
                recno = 0;
            }
        }
    }

解説解説:

行情報構造体をoffset分領域を取ります。

1文字づつデータを読み込み
改行文字が見つかった段階で
確保済み領域に行情報構造体を設定します。
行データは、ここで確保するためreallocを用いて確保した後bcopyにてコピーします。

行が確定するまでは、一時バッファspに格納されます。

spのreallocは理解できるけど
行情報構造体のreallocは、なぜと思ってしまう。
同じ領域に二度設定することがないので無駄な処理を入れなくてといいんじゃないかと思ってしまいますが

プログラムプログラム:

    if (ferror(fp)) {
        ierr(fn);
        rc = 1;
        goto done;
    }
    if (cnt) {
        llines[recno].l = sp;
        sp = NULL;
        llines[recno].len = cnt;
        if (++recno == off) {
            wrap = 1;
            recno = 0;
        }
    }

解説解説:

上の読み込み処理でエラーがないかチェックをしています。

また最後の行が開業されていない場合は、
その行のデータを保持します。

上の処理では、reallocしていますが
最後の処理なため一時バッファを構造体に詰めています。

せっかくならば無駄なことをしないといった感じなんでしょうか
少しだけ効率的にするのはいい心がけですし。
私も見習わないとな〜

プログラムプログラム:

    if (rflag) {
        for (cnt = recno - 1; cnt >= 0; --cnt)
            WR(llines[cnt].l, llines[cnt].len);
        if (wrap)
            for (cnt = off - 1; cnt >= recno; --cnt)
                WR(llines[cnt].l, llines[cnt].len);
    } else {
        if (wrap)
            for (cnt = recno; cnt < off; ++cnt)
                WR(llines[cnt].l, llines[cnt].len);
        for (cnt = 0; cnt < recno; ++cnt)
            WR(llines[cnt].l, llines[cnt].len);
    }
done:
    for (cnt = 0; cnt < off; cnt++)
        free(llines[cnt].l);
    free(sp);
    free(llines);
    return (rc);
}

解説解説:

byte関数のときとはことなり
rflagの処理がとてもシンプル

byte関数の作りが悪いというわけではなく
処理の違いのせいですが‥

配列を逆から読むか読まないかの違いです。

doneで解放処理をしています。
エラーの時もgotoで飛ぶようにしているため安心

カテゴリー: プログラム, 入門 | タグ: , , | コメント無し »

コメント