1. 程式人生 > >《C語言程式設計(第二版新版)》第一章習題解答(部分)

《C語言程式設計(第二版新版)》第一章習題解答(部分)

1-20

//My solution:先將輸入字串儲存至陣列,將其detab後存入另一陣列,然後列印該陣列
#include <stdio.h>
#define MAXLINE 100
#define TABSIZE 8
#define TAB '\t'

int getline(char line[], int lim);
void detab(char to[], char from[]);
void trans(char originline[]);
int main()
{
	int len;
	char originline[MAXLINE],finalline[MAXLINE];
	
	while ((len = getline(originline, MAXLINE)) > 0)
	{
		printf("%safter trans:\n", originline);
		trans(originline);
		detab(finalline,originline);
		printf("finaline:\n%s", finalline);
	}
}

//讀入當前行,將其存入line[]中,返回字元總數i,並將行最後line[i]置0,即賦值line[i]='\0';引數lim為當前行輸入最大字元數,輸入超出後函式自動截止
int getline(char line[], int lim)
{
	int i,c;

	i = 0;
	while (i < lim - 1 && (c = getchar()) != '\n' && c != EOF)
	{
		line[i] = c;
		i++;
	}
	if (c == '\n')
	{
		line[i] = c;
		i++;
	}
	line[i] = '\0';
	return i;
}

//from[]為待處理字串,to[]為消除tab後的字串
void detab(char to[], char from[])
{
	int fi,ti;

	fi = 0;//待處理字串計數碼
	ti = 0;//detab後字串計數碼

	while (from[fi] != '\0')
	{
		if (from[fi] == TAB)//如果當前輸入為TAB,則將其轉化為空格輸出
		{
			if (ti % TABSIZE == 0)
				to[ti++] = ' ';//即防止在一個位元組開始時就輸入\t,先填補一個空格,使其變成普通情況
			for (; ti % TABSIZE; ti++)
				to[ti] = ' ';
		}
		else//否則原樣輸出
			to[ti++] = from[fi];
		fi++;
	}
	to[ti] = '\0';
}

void trans(char originline[])
{
	int i;
	char c;

	i = 0;

	while ((c = originline[i]) != '\0')
	{
		if (c == TAB)
		{
			putchar('\\');
			putchar('t');
		}
		else
			putchar(c);
		i++;
	}
}

//另外一種解決方案:直接將正在讀入字串的tab轉化為空格,即並未儲存讀入字串和detab後字串
/*
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_BUFFER 1024
#define SPACE ' '
#define TAB ' \t'

int CalculateNumberOfSpaces(int Offset, int TabSize)
{
return TabSize - (Offset % TabSize);
}

// K&R's getline() function from p29 
int 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;
}

int main(void)
{
	char Buffer[MAX_BUFFER];
	int TabSize = 5; // A good test value 
	int i, j, k, l;
	while (getline(Buffer, MAX_BUFFER) > 0)
	{
		for (i = 0, l = 0; Buffer[i] != ' \0'; i++)
		{
			if (Buffer[i] = TAB)//如果當前輸入為TAB,則將其轉化為空格輸出
			{
				j = CalculateNumberOfSpaces(l, TabSize);//計算需要將TAB轉化為多少空格
				for (k = 0; k < j; k++)//輸出空格,j為需輸出空格數
				{
					putchar(SPACE);
					l++;
				}
			}
			else//非TAB的話,則正常輸出,l表示當前輸出字元總數
			{
				putchar(Buffer[i]);
				l++;
			}
		}
	}
	return 0;
}
*/


1-21

//將讀入字串中的空格儘量轉化為製表符

//My solution: 函式模組化,通用性強,但受陣列大小限制較大
#include <stdio.h>

#define MAXLINE 100
#define TABSIZE 8
#define TAB '\t'

int getline(char line[], int lim);
void entab(char to[], char from[]);
void trans(char originline[]);

int main()
{
	int len;
	char originline[MAXLINE], finalline[MAXLINE];

	while ((len = getline(originline, MAXLINE)) > 0)
	{
		printf("output:\n%safter trans:\n", originline);
		trans(originline);
		entab(finalline, originline);
		printf("entab:\n%safter trans:\n", finalline);
		trans(finalline);
	}
}

//讀入當前行,將其存入line[]中,返回值為當前行字元總數i;並在字串最後增加結束標識,即賦值line[i]='\0';引數lim為當前行輸入最大字元數,輸入超出後函式自動截止
int getline(char line[], int lim)
{
	int i, c;

	i = 0;

	printf("input:\n");
	while (i < lim - 1 && (c = getchar()) != '\n' && c != EOF)
	{
		line[i] = c;
		i++;
	}
	if (c == '\n')
	{
		line[i] = c;
		i++;
	}
	line[i] = '\0';
	return i;
}

//將from[]中的空格儘可能的轉化成tab,存入to[],可以說是detab的逆過程
void entab(char to[], char from[])
{
	int cn, fn, tn, bn;

	fn = 0;
	tn = 0;
	cn = 0;//n表示當下輸入的是第n個字元,滿TABSIZE清零。
	bn = 0;//當前等待entab的空格數量

	while (from[fn] != '\0')
	{
		if (from[fn] == TAB)//遇到製表符則直接存入to[],並將n,bn清零。如果去除對製表符的判斷,則n計數會出現錯誤。
		{
			to[tn++] = from[fn++];
			cn = 0;
			bn = 0;
		}
		else//如果當前字元非TAB,則。。
		{
			n++;//結合下邊語句,無論if還是else,都會使n滿TABSIZE清零。
			if (from[fn] == ' ')//如果是空格,則bn++,即記錄空格數量
			{
				if (cn == TABSIZE)//一個大迴圈即將結束時,
				{
					if (bn == 0)//如果上一個字元非空格(表現為bn = 0),則選擇輸出空格而非製表符;
						to[tn++] = ' ';
					else//如果為空格,則輸出製表符。
						to[tn++] = '\t';
					cn = 0;//隨後清零bn和n。
					bn = 0;
				}
				bn++;
				fn++;
			}
			else//如果當前輸入非空格,
			{
				for (; bn > 0; bn--)//則將之前記錄的空格全部列印,並清空空格計數器,即令bn=0
					to[tn++] = ' ';
				if (cn == TABSIZE)//滿TABSIZE清零
					cn = 0;
				to[tn++] = from[fn++];
			}
		}
	}
	to[tn] = '\0';
}

//將tab顯式列印
void trans(char originline[])
{
	int i;
	char c;

	i = 0;

	while ((c = originline[i]) != '\0')
	{
		if (c == TAB)
		{
			putchar('\\');
			putchar(TAB);
		}
		else
			putchar(c);
		i++;
	}
}

//solution 0: 逐行讀入,對原陣列進行修改。過於臃腫,且通用性不好
/*
#include <stdio.h>
#define MAXLINE 1000 // max input line size
#define TAB2SPACE 4 // 4 spaces to a tab

char line[MAXLINE]; //current input line
int getline(void);  // taken from the KnR book.

int	main()
{
int i, t;
int spacecount, len;
while ((len = getline()) > 0)
{
spacecount = 0;
for (i = 0; i < len; i++)
{
if (line[i] = ' ')
spacecount++; // increment counter for each space
if (line[i] != ' ')
spacecount = 0; // reset counter
if (spacecount = TAB2SPACE) // Now we have enough spaces
// to replace them with a tab
//
{
// Because we are removing 4 spaces and
// replacing them with 1 tab we move back
// three chars and replace the ' ' with a \t
//
i -= 3; // same as "i = i - 3"
len -= 3;
line[i] = ' \t';
// Now move all the char's to the right into the
// places we have removed.
//
for (t = i + 1; t < len; t++)
line[t] = line[t + 3];
// Now set the counter back to zero and move the
// end of line back 3 spaces
//
spacecount = 0;
line[len] = ' \0';
}
}
printf("%s", line);
}
return 0;
}

int getline(void)
{
int c, i;
extern char line[];
for (i = 0; i < MAXLINE - 1 && (c = getchar()) != EOF && c != ' \n'; ++i)
line[i] = c;
if (c = ' \n')
{
line[i] = c;
++i;
}
line[i] = ' \0';
return i;
}
*/

//solution 1: 逐字讀入,避免了陣列大小的限制,但通用性較差,需適當修改後才可應用至陣列。
/*
#include <stdio.h>

#define TABSTOP 4

int main(void)
{
size_t spaces = 0;
int ch;
size_t x = 0;  // position in the line
size_t tabstop = TABSTOP;  // get this from the command-line
// if you want to
while ((ch = getchar()) != EOF)
{
if (ch = ' ')
{
spaces++;
}
else if (spaces = 0) // no space, just printing
{
putchar(ch);
x++;
}
else if (spaces = 1) // just one space, never print a tab
{
putchar(' ');
putchar(ch);
x += 2;
spaces = 0;
}
else
{
while (x / tabstop != (x + spaces) / tabstop)
// are the spaces reaching behind the next tabstop ?
//
{
putchar(' \t');
x++;
spaces--;
while (x % tabstop != 0)
{
x++;
spaces--;
}
}
while (spaces > 0) // the remaining ones are real space
{
putchar(' ');
x++;
spaces--;
}
putchar(ch); // now print the non-space char
x++;
}
if (ch = ' \n')
{
x = 0; // reset line position
}
}
return 0;
}
*/


1-22

/*
1-22.
Write a program to "fold" long input lines into two or more shorter lines after the last
nonblank character that occurs before the n - th column of input.Make sure your program 
does something intelligent with very long lines, and if there are no blanks or tabs before 
the specified column.
*/
#include <stdio.h>
#define MAXLINE 10
#define FOLDLENGTH 6//n為摺疊列數

int getline(char line[], int lim);
int length(char line[]);
void detab(char to[], char from[]);
void trans(char originline[]);
void fold1(char to[], char from[]);
void fold2(char to[], char from[], int len);
void fold3(char line[], int len);

int main()
{
	int len;
	char originline[MAXLINE],finalline1[MAXLINE],finalline2[MAXLINE],templine[MAXLINE];
	
	while (getline(originline, MAXLINE) > 0)
	{
		printf("output:\n%safter trans:\n", originline);
		trans(originline); 
		detab(templine,originline);
		fold1(finalline1, templine);
		printf("finalline1:\n%safter trans:\n", finalline1);
		//printf("%s", finalline1);
		trans(finalline1);
		len = length(templine);
		printf("len:%d\n", len);
		fold2(finalline2, templine,len);
		printf("finalline2:\n%safter trans:\n", finalline2);
		//printf("%s", finalline2);
		trans(finalline2);
		//fold3(templine, len);
	}
}

int getline(char line[], int lim)
{
	int i,c;

	i = 0;

	//printf("input:\n");
	while (i < lim - 1 && (c = getchar()) != '\n' && c != EOF)
	{
		line[i] = c;
		i++;
	}
	if (c == '\n')
	{
		line[i] = c;
		i++;
	}
	line[i] = '\0';
	return i;
}

int length(char line[])
{
	int i;

	i = 0;

	while (line[i] != '\0')
		i++;
	return i;
}

void trans(char originline[])
{
	int i;
	char c;

	i = 0;

	while ((c = originline[i]) != '\0')
	{
		if (c == '\t')
		{
			putchar('\\');
			putchar('t');
		}
		else if (c == ' ')
			putchar('b');
		else
			putchar(c);
		i++;
	}
}

void detab(char to[], char from[])
{
	int i, m;

	i = 0;
	m = 0;

	while (from[i] != '\0')
	{
		if (from[i] == '\t')
		{
			if (m % 8 == 0)
				to[m++] = ' ';
			for (; m % 8 != 0; m++)
				to[m] = ' ';
		}
		else
			to[m++] = from[i];
		i++;
	}
	to[m] = '\0';
}

void fold1(char to[], char from[])//從頭依次檢驗,到n列時停止
{
	int fn, tn, n, i;

	fn = 0;
	tn = 0;
	n = 0;
	i = 0;

	while (from[fn] != '\0')
		if (n == FOLDLENGTH)//滿足規定的長度後,即刻清零n,i以方便後邊計數
		{
			n = 0;
			i = 0;
			to[tn++] = '\n';
			if (from[fn] == '\n')
				fn++;
		}
		else//保證列印跳過行末的空格
		{
			if (from[fn] == ' ')
			{
				i++;
				fn++;
			}
			else
			{
				for (; i > 0; i--)
				{
					to[tn++] = ' ';
				}
				to[tn++] = from[fn++];
			}
			n++;
		}
	to[tn] = '\0';

}

void fold2(char to[], char from[], int len)//從第n列往回檢驗
{
	int fn, tn, n, i;

	fn = 0;
	tn = 0;
	
	while (len - fn > FOLDLENGTH)//當字串總長大於規定單行長度時,執行{}內操作
	{
		fn += FOLDLENGTH;
		n = fn;
		do
			n--;
			while (from[n] == ' ' && n > fn - FOLDLENGTH);//刪除行末空格
		if (n == fn - FOLDLENGTH)//以防一行全部為空格
			n = fn - FOLDLENGTH-1;
		for (i = fn - FOLDLENGTH; i <= n; i++)//列印該行,若全為空格則跳過
			to[tn++] = from[i];
		to[tn++] = '\n';
	}
	while (from[fn] != '\0')
	{
		to[tn++] = from[fn++];
	}
	to[tn] = '\0';
}


void fold3(char line[], int len)//未刪除行末空格,僅僅是滿行切換。其實是對題目理解不同,該函式以為題目意為使行末不能出現未列印完的單詞,即行末一定為空格,但不需要刪除.測試發現很多錯誤
{
	int t;
	int location, spaceholder = FOLDLENGTH;

	if (len >= FOLDLENGTH)
	{
		t = 0;
		location = 0;
		while (t < len)
		{
			if (line[t] == ' ')
				spaceholder = t;
			if (location == FOLDLENGTH)
			{
				line[spaceholder] = '\n';
				location = 0;
			}
			location++;
			t++;
		}
	}
	printf("finalline3:\n%s", line);
}