C言語勉強中 - detab問題02

プログラミング言語C 第2版 ANSI規格準拠

プログラミング言語C 第2版 ANSI規格準拠

こないだの続き。

問題

P42 演習1-20

入力されたタブを、次のタブ・ストップまでのスペースをうめる適当な数のブランク(空白)で置き換えるプログラムdetabを書け。タブ・ストップの位置は、例えばn文字というように固定して考えよ。nは変数にすべきか、記号パラメータにすべきか?

できたー!

タブ文字が一行に2個以上あるような場合、2回目以降に登場するタブ文字の位置っていうのは、前回登場したタブ文字の影響を受けてるから、単純にタブ文字の現れた位置で計算してたらおかしくなったり、それが分からなくてデバッグ用のprintfを埋め込みまくったりとか。
そんなこんなを繰り返してなんとかできました。よかったよかった。

タブ幅をコマンドラインから取得してみようと思ったのですが、文字列を数値に変換するのが大変そうだったので諦めました。

ソースコード

#include <stdio.h>

#define MAXLINE 1000

// 関数のプロトタイプ宣言
void detab( char src[], char dest[], int tab_stop, int lim );
int get_tab_width( int pos, int tab_stop );
int get_line_from_stdin( char line[], int max );
void zerofill( char str[], int size );


/**
 * メイン
 */
int main( int argc, char* argv[] ) {
  int tab_stop = 4;   // タブの幅
  int line_len = -1;  // 一行の文字数

  char line[MAXLINE];      // 一行の文字列(変換前)
  char line_dest[MAXLINE]; // 一行の文字列(変換後)

  while( line_len != 0 ) {
    // 文字列変数を初期化
    zerofill( line, sizeof line );
    zerofill( line_dest, sizeof line_dest );
    // 標準入力から一行取得してlineに入れる
    line_len = get_line_from_stdin( line, MAXLINE );
    // lineのタブをスペースに変換してline_destに入れる
    detab( line, line_dest, tab_stop, MAXLINE );
    // 出力する
    printf( "%s", line_dest );
  }
}



/**
 * タブ文字を半角スペースに変換する。
 * 
 * @param src 変換前文字列
 * @param dest 変換後文字列
 * @param tab_stop タブ・ストップ
 * @param lim 変換後文字列の最大長
 */
void detab( char src[], char dest[], int tab_stop, int lim ) {
  int src_pos = 0;   // 変換前文字列の位置
  int dest_pos = 0;  // 変換後文字列の位置
  int tab_width = 0; // タブの長さ
  int i = 0;         // ループカウンタ
  
  // 文字列が終わるまでループ
  while( src[src_pos] != '\0' ) {
    if( src[src_pos] == '\t' ) {
      // タブの場合
      tab_width = get_tab_width( dest_pos, tab_stop );
      for( i=0; i<tab_width; i++ ) {
        dest[dest_pos++] = ' ';
        // コピー先が限界を超えたら終わり
        if( dest_pos > MAXLINE ) return;
      }
      src_pos++;
    } else {
      // タブ以外の場合
      dest[dest_pos++] = src[src_pos++];
    }
    // コピー先が限界を超えたら終わり
    if( dest_pos > MAXLINE ) return;
  }
}



/**
 * タブ文字の長さを計算する。
 *
 * @param pos タブ文字の位置(行頭から数える)
 * @param tab_stop タブ・ストップ
 */
int get_tab_width( int pos, int tab_stop ) {
  return tab_stop - ( pos % tab_stop );
}



/**
 * 標準入力から一行取得する。
 *
 * @param line 取得した文字列を入れる変数
 * @param max 読み込む最大文字数
 * @return 読み込んだ文字数
 */
int get_line_from_stdin( char line[], int max ) {
  int pos = 0;
  int i = 0;
  char c = 0;

  for( i=0; i<max-1; i++ ) {
    line[i] = '\0';
  }

  while( pos < max - 1 ) {
    c = getchar();
    if( c == EOF ) break;
    line[pos++] = c;
    if( c == '\n' ) break;
  }
  line[pos] = '\0';
  return pos;
}



/**
 * 配列の要素を0で埋める。
 *
 * @param str 配列
 * @param size 配列の長さ
 */
void zerofill( char str[], int size ) {
  int i = 0;
  for( i=0; i<size; i++ ) {
    str[i] = '\0';
  }
}