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

stconvから呼び出される2つの関数

stconv関数からは、コマンドラインで指定された変換方向によってtb2spとsp2tb関数のどちらかが呼び出されます。

tb2sp : タブ->スペース 変換(リスト10)

タブコードをスペースに変換するtb2sp関数は、引数も内部の処理もこれまでに紹介したものと同じです。

引数には、以下の3つを採ります。
char *src  : 変換前の文字列
char *dest : 返還後の文字列
int  tab   : タブストップ数

リスト10:タブからスペースに変換するtb2sp関数
void  tb2sp(char *src, char *dest, int tab)
{
  int   i, j, step;

  ↓ 送り側文字列の終端まで繰り返す
  for (i = 0; *src != '¥0'; i++) {
    ↓ タブコードが見付かったらスペースに変換
    if (*src == TAB) {
      ↓ 2バイト日本語への対処
      if ((i > 0) && (iskanji(*(src-1))))
        *dest++ = *src++;
      else {
        ↓ タブコードをスペースに置き換える
        step = tab - (i % tab);
        for (j = 0; j < step; j++)
          *dest++ = SPACE;
        src++;
        i += (j - 1);
      }
    }
    else
      *dest++ = *src++;
  }
  *dest = '¥0';
}

sp2tb : スペース->タブ 変換(リスト11)

スペースをタブコードに変換する関数です。引数は先述したtb2sp関数と同じです。
char *src  : 変換前の文字列
char *dest : 返還後の文字列
int  tab   : タブストップ数

srcの示す文字列を読み込み、タブコード位置に合わせて複数のスペースを1個のタブコードに変換していきます。

タブ→スペース変換の場合は、タブストップ幅によって置き換えるスペースの数が変わりました。スペース→タブ変換の場合は、タブストップ幅に従って適正な位置でスペースをタブに変換しなければなりません。

タブからスペースに変換する場合には、先頭から1文字ずつ読み取っていって、タブコードを見つけたら次のタブストップまでを複数のスペースに置き換える――という形で対処できました。

一方、スペースをタブに変換する場合は、まずスペースを順次読み取っていって
タブストップ位置にスペースがあれば
最初にスペースが見つかった位置に戻って
そこをタブコードに置き換える
という手順になります。そのため、tb2sp関数にはなかったchar型ポインタ変数posを使い、スペースが見つかったら変換元のsrcではなくposを1つ進めていく――という方法を採っています。

リスト11:スペースからタブに変換するsp2tb関数
void    sp2tb(char *src, char *dest, int tab)
{
  char  *pos;
  int   i, j;
  int   step;

  ↓ 送り側文字列の終端まで繰り返す
  for (i = 0; *src != '¥0'; i++) {
    step = tab - (i % tab);
    ↓ スペースが見付かったらタブコードに変換
    if (*src == SPACE)  {
      ↓ 2バイト日本語への対処
      if (((i > 0) && (iskanji(*(src-1)))) || (*(src+1) != SPACE))
        *dest++ = *src++;
      else {
        ↓ スペースをタブコードに置き換える
        pos = src;
        for (j = 0; j < step; j++, pos++)
          if (*pos != SPACE)
            break;
          if (j == step) {
            *dest++ = TAB;
             src = pos;
             i += (j - 1);
             ↑ iはforループ中でインクリメントされているため
                このときだけ1減算しておく
           }
           else
             *dest++ = *src++;
        }
      }
      else {
        *dest++ = *src++;
      }
  }
  *dest = '¥0';
}


Cのソースコードを読み込むことを前提に、タブとスペースを相互に変換するプログラムの作成と改造の手順を紹介してきました。

プログラム自体の構造やアルゴリズム(目的の処理を実現するための手順)を読み取ることも大切ですが、ここでは
最小限の機能を実現する処理を作り上げ
それを改造していくことで
機能を強化・追加していく過程
を見ていただくことを主眼としました ※1

いきなり長編小説を書こうとしても、うまくいかず途中で挫折します。短編小説で練習し、そこで鍛えた技を使って行くことで長編をモノにできます。漫画でも、四コマ漫画で起承転結を身に着けることが、長編作品の基礎体力になると言われます。

プログラミングもこれらと同じで、小さくてシンプルなプログラムをまず作り上げ、それに少しずつ手を加えて複雑なものにしていくのが効率的です。小さな成功を経験すれば自信も付きますし、バグに悩まされても『ひとつ前に成功していた段階』まで戻れば、容易に処理を組み立て直せます。

アルゴリズムについては、追って本コラムで詳しく取り上げる予定です