「ふつりな」第6章の練習問題(1)・続

『ふつうのLinuxプログラミング』に入門して、練習問題を解いています。

前回(http://d.hatena.ne.jp/torazuka/20110706/c)解けなかった問題の続き。

問題

タブ文字('\t')を「\t」、改行を「'$'+改行」として出力するcatコマンドを書きなさい。

前回、タブ文字だけまず何とかしようとしたけれど、惜しいところまでキタので、改行も追加。

解答

しかし、まだ正しく動きません。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void do_cat(const char *path);
static char *rep_str(char *str, const char *before, const char *after);
static void die(const char *s);

int main(int argc, char *argv[])
{
	int i;

	if(argc < 2){
		fprintf(stderr, "%s: file name not given.\n", argv[0]);
		exit(1);
	}

	for(i = 1; i < argc; i++){
		do_cat(argv[i]);
	}
	return 0;
}

static void do_cat(const char *path)
{
	FILE *f;
	char before_t[] = "\t";
	char after_t[] = "\\t";
	char before_n[] = "\n";
	char after_n[] = "$\n";
	char buff[256];

	f = fopen(path, "r");
	if(!f){
		die(path);
	}

	for(;;){
		buff[0] = '\0';
		if((fgets(buff, sizeof(buff), f)) == NULL){
			break;
		}
		rep_str(buff, before_t, after_t);
		rep_str(buff, before_n, after_n);
		fprintf(stdout, "%s", buff);
	}
}

static char *rep_str(char *str, const char *before, const char *after)
{
	int i;
	char *result = str;
	int str_len = strlen(str);
	int before_len = strlen(before);

	result[0] = '\0';

	for(i = 0; i < str_len; i++){
		if(0 == strncmp(&str[i], before, before_len)){
			strcat(result, after);
			i += before_len - 1;
		} else {
			strncat(result, &str[i], 1);
		}
	}
	return result;
}

static void die(const char *s)
{
	perror(s);
	exit(1);
}

実行すると、各行の1文字目が欠けてしまう

オロロン、オロロン、、、。

わからないこと

なぜ1文字目が欠けるのか。

  • do_cat関数の中で、fgets関数を使って読み込んだ時点では、欠けていない
  • rep_str関数の中で、strcat(strncat)関数を使って文字列を繋いだ時点で、戻り値が欠けている

最初は、次の初期化方法が間違っているのかと考えました。

	result[0] = '\0';

「\0」という1つの要素を持つ文字列配列に対して、2つ目(result[1])の位置から値が繋がれてしまうから、ダメなのか??

しかし、1文字目が欠けることとは無関係な気がする…。そもそも、これは「欠ける」というより、正確には「検索対象文字列の1文字目が繋がれていない」という状態なのでは…。

それに、初期化しないと、do_catでprintf関数で出力したときに、2行ずつ表示されてしまう。オーノー・・・

もうちょっと考えます。

改善したこと

  • fgetcで1文字ずつ読み込むのをやめ、fgetsで1行ずつ読み込むことにした
    • 戻り値がintからcharになり、変換で悩む必要がなくなった
  • 文字列置換については、サンプルを探して参考にした
    • C言語入門 13.基本アルゴリズム http://c-production.com/contents/c/sec13.html
    • 文字列が一致すれば変換後の文字列を、一致しなければ評価中の文字を、戻り値用の文字列に繋ぐ
    • (…という概要は理解しているけれども、中で使ってる関数を正しく理解してないのでは。だから欠けるのでは)