第37回
ファイルの扱い(2)~ファイル操作の実例

ファイルを扱うプログラムの例

fopen関数でファイルを開き、データを読み取って処理をするプログラムを作ってみましょう。

文字列検索プログラムの改良

前々回(第35回)で、広域変数の使用例として「文字列から文字列を探し出す」プログラム“sfind”を紹介しました。そのときは、検索元のファイルをリダイレクトを使って読み込むようにしていました。リスト3が、そのmain関数です。mainから呼び出される関数“search”は省略しています。

このプログラムを、
コマンドラインで指定したファイルから単語を探し出す
仕様に変更してみましょう。

プログラム名を“sfind”とすると、起動のための書式は以下のようになります。
sfind <ファイル名> <単語>

<ファイル名>で指定したファイルを読み出しモードでオープンし、<単語>の含まれる行を画面に出力します。

ソースはリスト4のようになります。先述したように、mainから呼び出される関数searchのソースコードには変更がありません。main関数でファイルをオープンし、そこから1行ずつ読み出すところが書き換えられています。

fgets関数で1行ずつ読み取る

標準入力からファイルを読み込む場合は、以下のようにwhileの条件式でgets関数を呼び出していました。
while (gets(buf)) {
  clmn = search(buf, argv[1]);
            :

そこを、以下のようにfopen関数でファイルのオープンを試み、成功したらwhileループに入るよう書き直しています。ファイルのオープンに失敗した場合はfopen関数がNULLを返すので、そのときにはprintf関数でエラーメッセージを表示させ、return (-1)として「エラー終了」を示す-1の終了コードを返して終了するようにしています。

whileの条件式ではファイルから1行(改行まで)を読み込むfgets関数を呼び出し、ファイルの1行分をchar型配列のbufに保存します。
if ((fp = fopen(fname, "r")) == NULL) {
  printf("Can not open file - %d¥n", fname);
  return (-1);
} else {
  while (fgets(buf, 1024, fp)) {
    clmn = search(buf, word);

fgets関数の書式は、以下のようになっています。

宣言 : char *fgets(char *s, int n, FILE *fp);
機能 : fpで指定したファイルから1行(改行まで)を読み出してsで示すバッファへ保存する。
nはsで示すバッファのバイト数。
戻り値 : 引数で指定したバッファ。失敗したか終わりに到達した場合はNULL。

リスト3:リダイレクトでファイルから文字列を探し出すプログラム(第35回のex3502.c
#include <stdio.h>
#include <string.h>

int count = 0;

/* ---------------------------------------------
    search -- sからtを探し出現位置を返す
    見付けた数(広域変数 count を)加算する
   --------------------------------------------- */
int search(const char *s, const char *t)
{
        :
      省略
}

/* ---------------------------------------------
   main
     標準入力から第1パラメータの文字列を探す
   --------------------------------------------- */
int main(int argc, char *argv[])
{
  char buf[1024 + 1];  /* 1行分の保存場所 */
  int line = 1;        /* 行番号カウンタ */
  int clmn;            /* 桁位置を保持 */

  /* 標準入力から1行ずつ受け取る */
  while (gets(buf)) {
    clmn = search(buf, argv[1]);

    /* 文字列が見つかったとき */
    if (clmn >= 1) {
      printf("%04d : Line %04d / Col %04d¥n",
               count, line, clmn);
    }
    line++;    /* 行番号を1増加 */
  }
  /* 広域変数から見つかった文字列の累計を表示 */
  printf("--------¥nTotal : %d word(s) found.¥n", count);
  return (0);
}

リスト4:コマンドラインで指定したファイルから単語を探し出すプログラム(ex3701.c
#include <stdio.h>
#include <string.h>

/* serach関数で見付けた文字列の数を保存 */
int count = 0;

/* ---------------------------------------------
    search -- sからtを探し出現位置を返す
    見付けた数(広域変数 count を)加算する
   --------------------------------------------- */
int search(const char *s, const char *t)
{
  int i;

  /* sの文字数分繰り返す */
  for (i=0; i<strlen(s); i++) {
    if (strncmp(s, t, strlen(t)) == 0) {
      /* tの含まれる行を出力 */
      printf("----> ¥'%s¥' in %s", t, s);
      count++;  /* 見付けた数の累計を加算 */
      /* 先頭からの字数なのでiを1増加 */
      return (i+1);
    }
    /* 1文字進める */
    s++;
    /* sがtより短くなれば終了 */
    if (strlen(t) > strlen(s)) {
      break;
    }
  }
  /* 見つからなければ-1を返す */
  return (-1);
}

/* ---------------------------------------------
   main
     標準入力から第1パラメータの文字列を探す
   --------------------------------------------- */
int main(int argc, char *argv[])
{
  FILE *fp;
  char buf[1024 + 1];    /* 1行分の保存場所 */
  int line = 1;          /* 行番号カウンタ */
  int clmn;              /* 桁位置を保持 */
  char fname[256 + 1];   /* ファイル名 */
  char word[128 + 1];    /* 検索文字列 */

  /* コマンドラインからファイル名と検索文字列を取得 */
  strcpy(fname, argv[1]);
  strcpy(word, argv[2]);

  /* ファイルのオープン */
  if ((fp = fopen(fname, "r")) == NULL) {
    printf("Can not open file - %s¥n", fname);
    return (-1);
  } else {
    while (fgets(buf, 1024, fp) != NULL) {
      clmn = search(buf, word);
      /* 文字列が見つかったとき */
      if (clmn >= 1) {
        printf("Line %04d / Column %04d¥n", line, clmn);
      }
      line++;    /* 行番号を1増加 */
    }
    /* 見つかった文字列の累計(count)を表示 */
    printf("--------¥nTotal : %d word(s) found.¥n", count);
  }
  return (0);
}

プログラムの実行結果

このプログラムでex3701.cから文字列“print”という語を検索した場合の実行結果は、画面1のようになります。

なお、今回はファイルのオープン処理をテーマとしたため、エラー処理はファイルがオープンできなかった場合のみとしました。本来なら、コマンドラインオプション(ファイル名と検索文字列)が不足していた場合にもエラー処理を行う必要があります。これについては、回を追って紹介します。



お陰さまで、本コラムも連載3周年を超えて4年目に突入しました。

読者のみなさんに感謝の気持ちを込めてプレゼントを用意しましたので、ご応募いただければ幸いです。

連載3周年!サイン入り著書プレゼント

読者の皆さまに、筆者が書いた以下の著書をプレゼントします。すべて直筆のサイン入り、ぜひ奮ってご応募ください。

プレゼント内容

1.「ベテランが丁寧に教えてくれるオペレーティングシステムの知識と実務」翔泳社
2.「ためしてナットクSQL 基礎編 データベースがよくわかる」ソフトバンククリエイティブ
3.「ためしてナットクSQL 応用編 データベースがよくわかる」ソフトバンククリエイティブ
4.「システム手帳式 誰でもプロ並み デジタル写真 撮影術」講談社
5.「デジタルカメラ 『プロ』が教える写真術」講談社
 各1名様


応募方法

メールの件名を「C言語コラム読者プレゼント」とし、本文に郵便番号、住所、氏名、メールアドレス、電話番号を記入のうえご応募ください。

宛先: Marketing.NET@grapecity.com
締切: 2009年10月30日(金) 18:00

プレゼントは厳正な抽選のうえ、11月上旬にお送りします。
当選者の発表はプレゼントの発送をもって代えさせていただきます。

※お送りいただいた個人情報は本企画の抽選や発送に必要な範囲内でのみ使用します。
※本企画にご応募いただいた場合、上記利用範囲ならびにグレープシティのプライバシーポリシーに同意していただいたものとします。


今後とも、「もう一度基礎からC言語」をよろしくお願いします。