K&R 演習1-20 解答 (プログラミング言語C 第2版)

スポンサーリンク

鍛錬 185

K&R 演習1-20 解答 (プログラミング言語C 第2版)

「入力されたタブを、次のタブストップまでのスペースをうめる適当な数のブランク(空白)で置き換えるプログラム detab を書け」という問題です。

プログラム

以下は、入力されたタブを、次のタブストップまでのスペースをうめる適当な数のブランク(空白)で置き換えるプログラム detab.c です。

// include
#include <stdio.h>

// preprocessor
#define MAXLINE  1000  // 入力可能な文字数の最大値
#define TAB_STOP 4     // タブストップの位置

// prototype
int my_getline(char line[], int maxline);
void copy(char to[], char from[]);
int detab(int len, int tab_point, char line[]);

// main
int main(void)
{
	char line[MAXLINE];
	int len;
	int count;
	int ret;
	int i;
	
	int tab_state;      // タブが文字列中に存在したか否かを表す
	int tab_exist;      // タブが存在した
	int tab_not_exist;  // タブが存在しなかった
	
	// 初期化
	tab_exist = 1;
	tab_not_exist = 0;
	tab_state = tab_not_exist;  // タブが存在しなかった
	
	count = 1;
	while ((len = my_getline(line, MAXLINE)) > 0) {
		printf("----- %d 回目 -----\n", count);
		printf("入力行の文字列 = %s", line);
		printf("入力行の長さ = %d\n", len);
		
		// タブを検索
		for (i = 0; i < len; ++i) {
			// タブが存在する場合
			if (line[i] == '\t') {
				// タブをブランクに置換
				ret = detab(len, i, line);
				len = ret;
				tab_state = tab_exist;  // タブが存在した
			}
		}
		
		// 文字列中にタブが存在した場合
		if (tab_state == tab_exist) {
			printf("置換後の長さ = %d\n", len);
			printf("置換後の文字列 = %s", line);
			printf("\n");
			tab_state = tab_not_exist;  // タブの存在確認を初期化
			++count;
		}
		// 存在しなかった場合
		else {
			printf("文字列中にタブは存在しなかった\n");
			printf("\n");
			++count;
		}
	}
	
	return 0;
}

// =====================================
// @brief      行を取得し,その長さを返す
// @param[in]  lim  入力行の最大長
// @param[out] s    取得した行
// @return     i -> 取得した行の長さ
// @note       無し
// =====================================
int my_getline(char s[], int lim)
{
	int c, i;
	
	for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
		s[i] = c;
	if (c == '\n') {
		s[i] = c;
		++i;
	}
	
	s[i] = '\0';
	
	return i;
}

// ===================================
// @brief      文字列をコピーする
// @param[in]  from  コピー元の文字列
// @param[out] to    コピー先の文字列
// @return     無し
// @note       無し
// ===================================
void copy(char to[], char from[])
{
	int i;
	
	i = 0;
	while ((to[i] = from[i]) != '\0')
		++i;
}

// ===================================================
// @brief         タブをブランクに置換する
// @param[in]     len        置換前の文字列長
// @param[in]     tab_point  タブが存在する配列の場所
// @param[in/out] line       文字列
// @return        i -> 置換後の文字列長
// @note          無し
// ===================================================
int detab(int len, int tab_point, char line[])
{
	char tmp_line[MAXLINE];
	int p;
	int i;
	
	// タブの次の位置を格納
	p = tab_point + 1;
	
	// コピー
	copy(tmp_line, line);
	
	// タブをブランクに置換
	for (i = 0; i < TAB_STOP; ++i) {
		line[tab_point] = ' ';
		++tab_point;
	}
	
	// タブより後の文字列を格納
	for (i = tab_point; (p < len) && (i < (MAXLINE - 1)); ++i,++p)
		line[i] = tmp_line[p];
	line[i] = '\0';
	
	return i;
}

実行結果

以下は、プログラム detab.c を次に示す順序で実行し、動作を確認しています。

※ 設問ではタブストップの位置を固定するように指示されているため、上記のプログラム detab.c では、4 文字ごとに固定しています。よって、1 つのタブを 4 つのブランクに置換します。

※ 入力行の長さには改行コードも含みます。
 

入力順序 入力した文字列 置換後の、ブランクの数
1 ABC(タブ)DEF 4
2 ABC(タブ)DEF(タブ)GHI 8
3 ABC(タブ)DEF(タブ)GHI(タブ)JKL 12
4 ABC(タブ)(タブ)DEF 8
5 ABCDEFGHIJKLMN 置換せず

 

***@ubuntu:~/***/test/c$ 
***@ubuntu:~/***/test/c$ gcc -Wall -Wextra detab.c -o detab
***@ubuntu:~/***/test/c$ ./detab
ABC	DEF
----- 1 回目 -----
入力行の文字列 = ABC	DEF
入力行の長さ = 8
置換後の長さ = 11
置換後の文字列 = ABC    DEF

ABC	DEF	GHI
----- 2 回目 -----
入力行の文字列 = ABC	DEF	GHI
入力行の長さ = 12
置換後の長さ = 18
置換後の文字列 = ABC    DEF    GHI

ABC	DEF	GHI	JKL
----- 3 回目 -----
入力行の文字列 = ABC	DEF	GHI	JKL
入力行の長さ = 16
置換後の長さ = 25
置換後の文字列 = ABC    DEF    GHI    JKL

ABC		DEF
----- 4 回目 -----
入力行の文字列 = ABC		DEF
入力行の長さ = 9
置換後の長さ = 15
置換後の文字列 = ABC        DEF

ABCDEFGHIJKLMN
----- 5 回目 -----
入力行の文字列 = ABCDEFGHIJKLMN
入力行の長さ = 15
文字列中にタブは存在しなかった
タイトルとURLをコピーしました