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

スポンサーリンク

鍛錬 342

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

文字列 s1 中の a-z のような省略記法を,それと等価な完全リスト abc・・・・・・xyz にして s2 中に展開する関数 expand(s1, s2) を書け。大文字,小文字,数字を許し, a-b-c や a-z0-9 や -a-z のような場合も処理できるようにせよ。先頭および最後の – は文字とみなすことにする。

B.W.カーニハン D.M.リッチー 石田晴久 訳 『プログラミング言語C 第2版 ANSI 規格準拠』, (共立出版, 2017), pp.77.

スポンサーリンク

プログラム

以下は、入力した文字列の省略記法を、それと等価な完全リストに展開するプログラム、kr_3_3.c です。

// include
#include <stdio.h>

// preprocessor
#define MAX_LINE   256   // 入力可能な文字数の最大値
#define MAX_EXPAND 2560  // 展開可能な文字数の最大値

// prototype
int my_getline(int lim, char s[]);
int expand(char s1[], char s2[]);
int FrontMultiple(char s1[], int s1_cnt, char basis, char s2[]);
int MiddleMultiple(char s1[], int s1_cnt, int s2_cnt, char s2[]);
void EndMultiple(char s1[], int s1_cnt, int s2_cnt, char basis, char s2[]);

// main
int main(void)
{
	char s1[MAX_LINE] = "";
	char s2[MAX_EXPAND] = "";
	int len;
	int ret;
	int i;
	
	printf("--------------------------------------\n");
	
	while ((len = my_getline(MAX_LINE, s1)) > 0) {
		// 改行コードを削除
		s1[len - 1] = '\0';
		printf("展開前 = %s\n", s1);
		
		ret = expand(s1, s2);
		if (ret == -1) {
			printf("ERROR,expand() ret=%d\n", ret);
			return 1;
		}
		
		printf("展開後 = %s\n", s2);
		printf("--------------------------------------\n");
		
		// 初期化
		for (i = 0; i < MAX_EXPAND; i++) {
			s2[i] = '\0';
		}
	}
	
	return 0;
}

// ======================================
// @brief      行を取得し,その長さを返す
// @param[in]  lim  入力行の最大長
// @param[out] s    取得した行
// @return     i -> 取得した行の長さ
// @note       無し
// ======================================
int my_getline(int lim, char s[])
{
	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      a-z のような省略記法を,
// @brief      それと等価な完全リスト abc...xyz に変換する
// @param[in]  s1  変換前の文字列
// @param[out] s2  変換後の文字列
// @return     0 -> 成功
// @return     -1 -> エラー
// @note       無し
// ========================================================
int expand(char s1[], char s2[])
{
	char basis;
	int len;
	int s1_cnt, s2_cnt;
	int i;
	
	// 文字列チェック及び,文字列の長さを取得
	i = 0;
	while (s1[i] != '\0') {
		if ((s1[i] == '-') ||
			(s1[i] >= 'a' && s1[i] <= 'z') ||
			(s1[i] >= 'A' && s1[i] <= 'Z') ||
			(s1[i] >= '0' && s1[i] <= '9')) {
			i++;
		}
		else {
			printf("ERROR,'-'、アルファベット、数字の"
				   "いずれでもない文字が含まれている\n");
			return -1;
		}
	}
	len = i;
	if (len == 0) {
		s2[0] = '\0';
		return 0;
	}
	
	// 初期化
	s1_cnt = 0;
	s2_cnt = 0;
	
	// 先頭が '-' の場合
	if (s1[s1_cnt] == '-') {
		s1_cnt++;
		
		// -a ~ -z の,いずれかの場合
		if (s1[s1_cnt] >= 'a' && s1[s1_cnt] <= 'z') {
			basis = 'a';
			s2_cnt = FrontMultiple(s1, s1_cnt, basis, s2);
		}
		// -A ~ -A の,いずれかの場合
		else if (s1[s1_cnt] >= 'A' && s1[s1_cnt] <= 'Z') {
			basis = 'A';
			s2_cnt = FrontMultiple(s1, s1_cnt, basis, s2);
		}
		// -0 ~ -9 の,いずれかの場合
		else if (s1[s1_cnt] >= '0' && s1[s1_cnt] <= '9') {
			basis = '0';
			s2_cnt = FrontMultiple(s1, s1_cnt, basis, s2);
		}
		
		s1_cnt++;
	}
	
	while (s1[s1_cnt] != '\0') {
		// アルファベットまたは数字の場合
		if (s1[s1_cnt] != '-') {
			s2[s2_cnt] = s1[s1_cnt];
			s1_cnt++;
			s2_cnt++;
		}
		
		// '文字''-''文字' の場合
		else if ((s1[s1_cnt] == '-') && (s1[s1_cnt + 1] != '\0')) {
			s1_cnt--;
			s2_cnt = MiddleMultiple(s1, s1_cnt, s2_cnt, s2);
			s1_cnt = s1_cnt + 3;
		}
		
		// 終端が '-' の場合
		else if ((s1[s1_cnt] == '-') && (s1[s1_cnt + 1] == '\0')) {
			s1_cnt--;
			
			// a- ~ z- の,いずれかの場合
			if (s1[s1_cnt] >= 'a' && s1[s1_cnt] <= 'z') {
				basis = 'z';
				EndMultiple(s1, s1_cnt, s2_cnt, basis, s2);
			}
			// A- ~ Z- の,いずれかの場合
			else if (s1[s1_cnt] >= 'A' && s1[s1_cnt] <= 'Z') {
				basis = 'Z';
				EndMultiple(s1, s1_cnt, s2_cnt, basis, s2);
			}
			// 0- ~ 9- の,いずれかの場合
			else if (s1[s1_cnt] >= '0' && s1[s1_cnt] <= '9') {
				basis = '9';
				EndMultiple(s1, s1_cnt, s2_cnt, basis, s2);
			}
			
			return 0;
		}
	}
	
	return 0;
}

// =========================================================
// @brief      文字列先頭に '-' が存在し,
// @brief      その次の文字がアルファベットまたは数字の場合
// @param[in]  s1      変換前の文字列
// @param[in]  s1_cnt  s1 における現在の要素番号
// @param[in]  basis   s2 に展開する文字列の内,最初の文字
// @param[out] s2      変換後の文字列
// @return     n -> s2 における現在の要素番号
// @note       無し
// =========================================================
int FrontMultiple(char s1[], int s1_cnt, char basis, char s2[])
{
	char c;
	int n;
	
	for (n = 0; (c = basis + n) < s1[s1_cnt]; n++) {
		s2[n] = c;
	}
	s2[n] = s1[s1_cnt];
	n++;
	
	return n;
}

// ================================================
// @brief      '文字''-''文字' の並びを展開する
// @param[in]  s1      変換前の文字列
// @param[in]  s1_cnt  s1 における現在の要素番号
// @param[in]  s2_cnt  s2 における現在の要素番号
// @param[out] s2      変換後の文字列
// @return     s2_cnt -> s2 における現在の要素番号
// @note       無し
// ================================================
int MiddleMultiple(char s1[], int s1_cnt, int s2_cnt, char s2[])
{
	char front, back;
	char c;
	int n;
	
	// a ~ z の,いずれかの場合
	if (s1[s1_cnt] >= 'a' && s1[s1_cnt] <= 'z') {
		front = s1[s1_cnt];
		s1_cnt = s1_cnt + 2;
		back = s1[s1_cnt];
		
		for (n = 1; (c = front + n) < back; n++) {
			s2[s2_cnt] = c;
			s2_cnt++;
		}
		s2[s2_cnt] = back;
	}
	
	// A ~ Z の,いずれかの場合
	if (s1[s1_cnt] >= 'A' && s1[s1_cnt] <= 'Z') {
		front = s1[s1_cnt];
		s1_cnt = s1_cnt + 2;
		back = s1[s1_cnt];
		
		for (n = 1; (c = front + n) < back; n++) {
			s2[s2_cnt] = c;
			s2_cnt++;
		}
		s2[s2_cnt] = back;
	}
	
	// 0 ~ 9 の,いずれかの場合
	if (s1[s1_cnt] >= '0' && s1[s1_cnt] <= '9') {
		front = s1[s1_cnt];
		s1_cnt = s1_cnt + 2;
		back = s1[s1_cnt];
		
		for (n = 1; (c = front + n) < back; n++) {
			s2[s2_cnt] = c;
			s2_cnt++;
		}
		s2[s2_cnt] = back;
	}
	
	s2_cnt++;
	
	return s2_cnt;
}

// =======================================================
// @brief      '文字''-''\0' の並びを展開する
// @param[in]  s1      変換前の文字列
// @param[in]  s1_cnt  s1 における現在の要素番号
// @param[in]  s2_cnt  s2 における現在の要素番号
// @param[in]  basis   s2 に展開する文字列の内,最後の文字
// @param[out] s2      変換後の文字列
// @return     無し
// @note       無し
// =======================================================
void EndMultiple(char s1[], int s1_cnt, int s2_cnt, char basis, char s2[])
{
	char c;
	int n;
	
	if (s1[s1_cnt] == basis) {
		s2[s2_cnt] = '\0';
		return;
	}
	
	for (n = 1; (c = s1[s1_cnt] + n) < basis; n++) {
		s2[s2_cnt] = c;
		s2_cnt++;
	}
	s2[s2_cnt] = basis;
	s2_cnt++;
	s2[s2_cnt] = '\0';
}
スポンサーリンク

実行結果

以下は、プログラム kr_3_3.c を実行しています。

***@ubuntu:~/***/test/c$ 
***@ubuntu:~/***/test/c$ gcc -Wall -Wextra kr_3_3.c -o kr_3_3
***@ubuntu:~/***/test/c$ ./kr_3_3
--------------------------------------
-m
展開前 = -m
展開後 = abcdefghijklm
--------------------------------------
-M
展開前 = -M
展開後 = ABCDEFGHIJKLM
--------------------------------------
-7
展開前 = -7
展開後 = 01234567
--------------------------------------
m-
展開前 = m-
展開後 = mnopqrstuvwxyz
--------------------------------------
M-
展開前 = M-
展開後 = MNOPQRSTUVWXYZ
--------------------------------------
7-
展開前 = 7-
展開後 = 789
--------------------------------------
m-p5-7
展開前 = m-p5-7
展開後 = mnop567
--------------------------------------
-m-p
展開前 = -m-p
展開後 = abcdefghijklmnop
--------------------------------------
a-b-c
展開前 = a-b-c
展開後 = abc
--------------------------------------
-cR-W7-
展開前 = -cR-W7-
展開後 = abcRSTUVW789
--------------------------------------
タイトルとURLをコピーしました