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

スポンサーリンク

鍛錬 190

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

「ブランクの列を同じスペーシングを行う最小の数のタブ及びブランクで置き換えるプログラム entab を書け」という問題です。

プログラム

以下は、ブランクの列を同じスペーシングを行う最小の数のタブ及びブランクで置き換えるプログラム entab.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 entab(int len, int blank_point, char line[]);

// main
int main(void)
{
	char line[MAXLINE];  // 編集前の入力した文字列
	int len;
	int count;
	int ret;
	int i;
	
	int blank_state;      // ブランクが文字列中に存在したか否かを表す
	int blank_exist;      // ブランクが存在した
	int blank_not_exist;  // ブランクが存在しなかった
	
	// 初期化
	blank_exist = 1;
	blank_not_exist = 0;
	blank_state = blank_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] == ' ') {
				// ブランクを置換
				ret = entab(len, i, line);
				len = ret;
				blank_state = blank_exist;  // ブランクが存在した
			}
		}
		
		// 文字列中にブランクが存在した場合
		if (blank_state == blank_exist) {
			printf("置換後の長さ = %d\n", len);
			printf("置換後の文字列 = %s", line);
			printf("\n");
			blank_state = blank_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]     blank_point  ブランクが存在する配列の場所
// @param[in/out] line         文字列
// @return        i -> 置換後の文字列長
// @note          無し
// =============================================================
int entab(int len, int blank_point, char line[])
{
	char tmp_line[MAXLINE];
	int tab_num;             // 必要なタブの数
	int blank_num;           // 必要なブランクの数
	int p;
	int count;
	int i;
	
	// 連続するブランクの個数を確認
	count = 0;
	for (i = blank_point; i < len; ++i) {
		// ブランクではない場合
		if (line[i] != ' ')
			break;
		++count;
	}
	
	// 1個もしくは連続するブランクの,次の位置を格納
	p = i;
	
	// 初期化
	tab_num = 0;
	blank_num = 0;
	
	// ブランクの数がタブストップ以上の場合
	if (count >= TAB_STOP) {
		// 必要なタブの数を計算
		tab_num = count / TAB_STOP;
		
		// 必要なブランクの数を計算
		blank_num = count - (tab_num * TAB_STOP);
	}
	// ブランクの数がタブストップに満たない場合
	else
		// 必要なブランクの数を格納
		blank_num = count;
	
	// コピー
	copy(tmp_line, line);
	
	// ブランクをタブに置換
	for (i = 0; i < tab_num; ++i) {
		line[blank_point] = '\t';
		++blank_point;
	}
	
	// タブストップに満たない残りのブランクを格納
	for (i = 0; i < blank_num; ++i) {
		line[blank_point] = ' ';
		++blank_point;
	}
	
	// ブランクより後の文字列を格納
	for (i = blank_point; p < len; ++i,++p)
		line[i] = tmp_line[p];
	line[i] = '\0';
	
	return i;
}

実行結果

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

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

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

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

 

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

ABC    DEF
----- 2 回目 -----
入力行の文字列 = ABC    DEF
入力行の長さ = 11
置換後の長さ = 8
置換後の文字列 = ABC	DEF

ABC      DEF
----- 3 回目 -----
入力行の文字列 = ABC      DEF
入力行の長さ = 13
置換後の長さ = 10
置換後の文字列 = ABC	  DEF

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

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