第42回
仕様設計からコーディングまで~タブ/スペース変換プログラムを作る(2)

ファイルを指定できるよう改良する

前回作ったタブ→スペース変換プログラム"tb2sp"はフィルタだったので、変換元のテキストファイル(入力ファイル)はリダイレクトで指定する必要がありました。しかし、プログラムとしての使い勝手を考えれば、入力ファイルはコマンドライン・オプションで指定できる方が便利です。

ファイルを1つだけ指定

最終的には、複数のファイルをまとめて指定し、変換結果を一気に出力できるようにようにしたいのですが、今の段階では『入力ファイルを1つだけ』指定できる形にしておきます。

今回作るプログラムの書式は、以下のようになります。
tb2sp <タブストップ幅> <入力ファイル名>

処理結果はこれまでどおり標準出力へ送られるので、それをファイルに保存するならリダイレクトを使う必要があります。

たとえば"abc.c"をタブストップ幅=4でスペースに変換し、その結果を"xyz.txt"へ保存するなら、コマンドラインは以下のようになります。
tb2sp 4 abc.c > xyz.txt

main関数だけ書き換える

タブからスペースへと変換する処理――tb2sp関数の仕様は前回紹介したものと同じで構わないので、ソースに変更はありません。main関数だけをリスト1のように変更します。

tb2sp関数を含む完全なソースは、"ex4201.c"というファイル名でサンプルとして用意しています。

前回紹介したフィルタ版との違いは、コマンドライン・オプションのチェックでファイル名が指定されているかを調べる処理と、コマンドライン・オプションで指定された入力ファイルをオープンする処理が追加されたことです。

リスト1:コマンドライン・オプションで入力ファイルを指定できるようにした“tb2sp.c”のmain関数部分
int  main(int argc, char *argv[])
{
  char  rbuf[BUFSIZE + 1]; /* 読み込みバッファ */
  char  wbuf[BUFSIZE + 1]; /* 書き出しバッファ */
  int   n;
  FILE  *fp;

  if ((argv[1] == NULL) || (argv[2] == NULL)) {
    /* メッセージは標準出力に出力する*/
    fputs(
      "TABCONV : タブストップ幅(1-16)とファイル名を指定してください.",
       stderr);
    exit(-1);
  }

  n = atoi(argv[1]);    /* オプション文字を数値に直す */
  if (n < MIN_TAB)
    n = DEFAULT_TAB;
  else if (n > MAX_TAB)
    n = MAX_TAB;

  if ((fp = fopen(argv[2], "r")) == NULL) {
    fprintf(stderr, "%s がオープンできません.¥n", argv[2]);
    exit(-1);
  }

  fprintf(stderr, "タブストップ間隔 : %d¥n", n);
    while (fgets(rbuf, BUFSIZE, fp) != NULL) {
      tb2sp(rbuf, wbuf, n);
      fputs(wbuf, stdout);
    }
  fprintf(stderr, "¥a¥t---- 変換終了しました!!¥n");
}

main関数の変更点

main関数の変更点を説明しておきましょう。

・コマンドライン・オプションのチェック

第1引数(argv[1]=タブストップ幅)と第2引数(argv[2]=入力ファイル名)のどちらかがNULLであれば、標準エラー出力にメッセージを表示してプログラムを終了します。

そうでなければ(第1、第2引数がともに入力されていれば)、変換処理を続けます。

↓コマンドライン・オプションが足りなければエラーで終了する
if ((argv[1] == NULL) || (argv[2] == NULL)) {
  fputs("TABCONV : タブストップ幅(1-16)とファイル名を指定してください.",
         stderr);
  exit(-1);
}

・ファイルのオープン

入力ファイル名を指定した第2引数(argv[2])をfopen関数の引数にしてファイルをオープンします。NULLが返ってきた場合は標準エラー出力にメッセージを出力してプログラムを終了し、そうでなければ処理を続けます。
if ((fp = fopen(argv[2], "r")) == NULL) {
  fprintf(stderr, "%s がオープンできません.¥n", argv[2]);
  exit(-1);
}