第1章-导言-习题1.18-1.23
1-18
思路是改动getline函数,读入某个输入行后,从该输入行尾部开始往前读,一直读到非空格或非制表符为止。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 #define MAXLINE 1000 3 4 int getline(char line[], int maxline, int pos); 5 void count(char s[]); 6 7 /* 16/06/10 删除每个输入行末尾的空格及制表符,并删除完全是空格的行 */ 8 main() 9 { 10 int len; 11 int i = 0, j = 0; 12 char line_dat[MAXLINE]; 13 int position = 0; 14 15 while ((len = getline(line_dat,MAXLINE,position)) > 0) { //根据教材【P21】改写,避免一开始就写入数据 16 position = len; 17 } 18 line_dat[position] = '\0'; //整个文本输入完毕后,在保存数据的字组line_dat末尾加入\0表征字符串的结尾 19 20 printf("%s", line_dat); 21 count(line_dat); 22 } 23 24 int getline(char s[], int lim, int pos) 25 { 26 int c, i, j; 27 for (i = pos; i < lim - 1 && (c = getchar()) != EOF&&c != '\n'; ++i) 28 s[i] = c; 29 if (c == EOF) { //如果到达文本末尾,就返回-1,终止main的while循环 30 return -1; 31 } 32 if (c == '\n') { 33 for (j = i - 1; (j > pos) && ((s[j] == ' ') || (s[j] == '\t')); j--); //从尾部开始往前读,一直读到非空格或非制表符为止 34 if (j == pos && s[pos] == ' ') j = pos; //全是空格的输入行 35 else { 36 j++; 37 s[j++] = '\n'; 38 } 39 } 40 return j; 41 } 42 43 /* 为了检验是否删除了空格或制表符,将教材【P15】1-6的统计次数的程序单独写成函数 */ 44 void count(char s[]) 45 { 46 int i, nwhite, nother; 47 int ndigit[10]; 48 49 nwhite = nother = 0; 50 for (i = 0; i < 10; i++) 51 ndigit[i] = 0; 52 53 for (i = 0; s[i] != '\0'; i++) { 54 if (s[i] >= '0'&&s[i] <= '9') 55 ++ndigit[i]; 56 else if (s[i] == ' ' || s[i] == '\n' || s[i] == '\t') 57 ++nwhite; 58 else 59 ++nother; 60 } 61 62 printf("ndigit="); 63 for (i = 0; i < 10; i++) 64 printf("% d", ndigit[i]); 65 printf(", white space = %d, other = %d\n", nwhite, nother); 66 }
答案也是采取了从尾部往前读一直到非空格/非制表符为止,不过仍只能处理单行输入的情况,不如我的程序好,而且不像我的程序那样使用void count(char s[])来检验,无法直观感觉到底删除没有。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 #define MAXLINE 1000 3 4 int getline(char s[], int lim); 5 int removetail(char s[]); 6 void count(char s[]); 7 8 /* 16/06/16 答案也是采取了从尾部往前读一直到非空格/非制表符为止,不过仍只能处理单行输入的情况,不如我的程序好,而且不像我的程序那样使用void count(char s[])来检验,无法直观感觉到底删除没有 */ 9 main() 10 { 11 int len; 12 char line_dat[MAXLINE]; 13 14 /* 下面的代码段将输入文本的内容保存在同一个数组line_dat里面,回车用\n取代 */ 15 while ((len = getline(line_dat, MAXLINE)) > 0) { 16 if (removetail(line_dat) > 0) { 17 printf("%s", line_dat); 18 } 19 count(line_dat); 20 } 21 return 0; 22 } 23 24 int removetail(char s[]) 25 { 26 int i; 27 28 i = 0; 29 while (s[i] != '\n') 30 i++; 31 i--; 32 for (; (i >= 0) && (s[i] == ' ' || s[i] == '\t'); i--); 33 if (i >= 0) { 34 i++; 35 s[i]='\n'; 36 i++; 37 s[i]='\0'; 38 } 39 return i; 40 } 41 42 int getline(char s[], int lim) 43 { 44 int c, i; 45 46 for (i = 0; (c = getchar()) != EOF && c != '\n'; ++i) { 47 if (i < lim - 2) { 48 s[i] = c; 49 } 50 } 51 if (c == '\n') { 52 s[i] = c; 53 i++; 54 } 55 s[i] = '\0'; 56 return i; 57 } 58 /* 为了检验是否删除了空格或制表符,将教材【P15】1-6的统计次数的程序单独写成函数 */ 59 void count(char s[]) 60 { 61 int i, nwhite, nother; 62 int ndigit[10]; 63 64 nwhite = nother = 0; 65 for (i = 0; i < 10; i++) 66 ndigit[i] = 0; 67 68 for (i = 0; s[i] != '\0'; i++) { 69 if (s[i] >= '0'&&s[i] <= '9') 70 ++ndigit[i]; 71 else if (s[i] == ' ' || s[i] == '\n' || s[i] == '\t') 72 ++nwhite; 73 else 74 ++nother; 75 } 76 77 printf("ndigit="); 78 for (i = 0; i < 10; i++) 79 printf("% d", ndigit[i]); 80 printf(", white space = %d, other = %d\n", nwhite, nother); 81 }
加入void count(char s[]),输出结果如下
从运行结果得知,这一程序并没有删除完全是空格的行,而只是不显示而已。
1-19
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 #define MAXLINE 1000 3 4 int getline(char s[], int lim); 5 void reverse(char s[]); 6 7 /* 16/06/16 将单个输入行的字符顺序颠倒过来,在1-16-a基础上加入reverse */ 8 main() 9 { 10 int len; 11 int i = 0, j = 0; 12 char line_dat[MAXLINE]; 13 14 while ((len = getline(line_dat, MAXLINE)) >0) { 15 reverse(line_dat); 16 printf("%s", line_dat); 17 } 18 return 0; 19 } 20 21 int getline(char s[], int lim) 22 { 23 int c, i; 24 25 for (i = 0; (c = getchar()) != EOF && c != '\n'; ++i) { 26 if (i < lim - 2) { 27 s[i] = c; 28 } 29 } 30 if (c == '\n') { 31 s[i] = c; 32 i++; 33 } 34 s[i] = '\0'; 35 return i; 36 } 37 38 void reverse(char s[]) 39 { 40 int i,j; 41 int temp; 42 43 i = 0; 44 while (s[i++] != '\n'); 45 i--; 46 /* 47 //自己写的 48 for (j = 0; j < (i+1 )/ 2; j++) { 49 temp = s[i - j]; 50 s[i - j] = s[j]; 51 s[j] = temp; 52 } 53 */ 54 //答案写的,因为i-- 55 j = 0; 56 while (j < i) { 57 temp = s[j]; 58 s[j] = s[i]; 59 s[i] = temp; 60 i--; 61 j++; 62 } 63 }
答案和我的函数reverse不同之处只在于交换赋值的方法不同而已,实际运行效果相同。
1-20(和1-21一起看,做后面第5章的习题5-11和5-12需要回头看一下这里)
当时没看明白习题说的是什么意思,直接看的答案。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 3 #define TABING 4 4 5 /* 16/06/13 将输入中的制表符替换为适当数目的空格,参考了答案 */ 6 main() 7 { 8 int c; 9 int pos, nt; 10 11 nt = 0; 12 pos = 1; //注意pos是从1开始统计的 13 while ((c = getchar()) != EOF) { 14 if (c == '\t') { 15 nt = TABING - (pos - 1) % TABING; //这一句是核心 16 while (nt > 0) { 17 putchar(' '); 18 pos++; 19 nt--; 20 } 21 } 22 else if (c == '\n') { 23 putchar(c); 24 pos = 1; 25 } 26 else { 27 putchar(c); 28 pos++; 29 } 30 } 31 }
运行结果如下(TABING设置为4,第一行为输入行,两个空均是Tab,第二行为按下Enter后的输出结果):
可以看到刚开始按下Tab系统默认占8个空,按下Enter后则以四个空格位为单位进行填充的。TABING是符号常量,nt = TABING - (pos - 1) % TABING;是这一个程序的核心之处。
1-21
习题不太好理解,参考一下下面的两个链接。
http://www.cnblogs.com/wzm-xu/p/4200206.html
http://www.cnblogs.com/Capricorn-Black/p/4665587.html
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 3 #define TABING 6 4 5 /* 16/06/16 这个程序应该是第1章最难理解的程序 */ 6 main() 7 { 8 int c, nb, nt, pos; 9 nb = 0; 10 nt = 0; 11 for (pos = 1; (c = getchar()) != EOF; ++pos) { 12 if (c == ' ') { 13 if (pos%TABING != 0) 14 ++nb; 15 else { 16 nb = 0; 17 nt++; 18 } 19 } 20 else { 21 for (; nt > 0; --nt) 22 putchar('\t'); 23 if (c == '\t') 24 nb = 0; 25 else 26 for (; nb > 0; --nb) 27 putchar(' '); 28 putchar(c); 29 if (c == '\n') 30 pos = 0; 31 else if (c == '\t') 32 pos = pos + (TABING - (pos - 1) % TABING) - 1; 33 } 34 } 35 }
输出结果(注意define TABING 6)为
这里简单分析一下:
(1)hello world
hello和world之间有3个空格
先打印出hello,然后遇到了第一个空格,此时pos = 6,且pos在制表符终止位上,所以执行else,nb=0,nt++;
接下来遇到第二个空格,pos = 7,且pos不在制表符终止位上,所以执行if,nb++,第三个空格也是如此,nb=2;
直到pos = 9 时,根据此前的nt=1,nb=2打印1个Tab(仍是系统默认的8)和2个空格,所以一共5个空格;
随后打印出world,程序结束。
(2)he is a boy
he后是一个tab,is后一个tab和两个空格,a后8个空格
这里不做分析。
(3)he is a boy
he后是一个tab,is后一个tab和两个空格,a后8个空格一个tab
当pos =3时,遇到第一个tab,令nb = 0,然后根据公式:pos = pos+(TABINC-(pos-1)%TABINC)-1得出pos=6,既是直接打印一个制表符,并跳到TABINC处,接着打印出is。
pos=11时遇到tab,由公式得出pos=12,跳到第二个TABINC处。此时后面是两个空格,nb=2,然后打印完两个空格后遇到a,此时pos=15。
a后是8个空格,pos=16/17的空格使得nb=2,然后pos=18的空格执行了else导致nb=0,nt=1,接下来的5个空格使得nb=5;
接下来遇到第4个TABINC,先把nt=1的第3个Tabing打印出来,然后nb = 0,再打印这第4个Tabing,这样的话a和boy中间应该有13个空格(第3个Tabing的5个加第4个Tabing的8个);
最后打印出boy,程序结束。
1-22
先自己写了一个,
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 3 #define LENGTH 10 4 #define MAXLINE 100 5 6 /* 16/06/13 编写一个程序,把较长的输入行折成短一些的两行或多行 */ 7 main() 8 { 9 int c; 10 int nc; 11 int i,j; 12 char line[MAXLINE]; 13 14 nc = 0; 15 j = 0; 16 for (i = 0; i < MAXLINE; i++) { 17 line[i] = 0; 18 } 19 while ((c = getchar()) != EOF) { 20 line[j++]=c; 21 nc++; 22 if (nc >= LENGTH) { 23 nc = 0; 24 while (line[j--] == ' '); 25 line[++j] = '\n'; 26 ++j; 27 } 28 } 29 line[j] = '\0'; 30 printf("%s\n", line); 31 }
输出结果如下,每行的折合数为10个字符
从答案来看,我原来写程序时把练习理解简单了。答案程序能够完成的功能包括:把制表符扩展为空格;每遇到一个换行符就将此前的输入文本打印出来;每当变量pos的值达到MAXCOL时,就会对输入行进行折叠。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 3 #define MAXCOL 10 4 #define TABING 8 5 6 char line[MAXCOL]; 7 8 void printl(int pos); 9 int exptab(int pos); 10 int findblnk(int pos); 11 int newpos(int pos); 12 13 /* 16/06/20 把制表符扩展为空格;每遇到一个换行符就将此前的输入文本打印出来;每当变量pos的值达到MAXCOL时,就会对输入行进行折叠*/ 14 main() 15 { 16 int c, pos; 17 18 pos = 0; 19 while ((c = getchar()) != EOF) { 20 line[pos] = c; 21 if (c == '\t') 22 pos = exptab(pos); 23 else if (c == '\n') { 24 printl(pos); 25 pos = 0; 26 } 27 else if (++pos >= MAXCOL) { 28 pos = findblnk(pos); 29 printl(pos); 30 pos = newpos(pos); 31 } 32 33 } 34 } 35 36 void printl(int pos) 37 { 38 int i; 39 for (i = 0; i < pos; i++) 40 putchar(line[i]); 41 if (pos > 0) 42 putchar('\n'); 43 44 } 45 46 int exptab(int pos) 47 { 48 line[pos] = ' '; 49 for (++pos; pos < MAXCOL && pos%TABING != 0; ++pos) 50 line[pos] = ' '; 51 if (pos < MAXCOL) 52 return pos; 53 else { 54 printl(pos); 55 return 0; 56 } 57 } 58 59 int findblnk(int pos) 60 { 61 pos--; 62 while (pos > 0 && line[pos] != ' ') 63 --pos; 64 if (pos == 0) 65 return MAXCOL; 66 else return(pos + 1); 67 } 68 69 int newpos(int pos) 70 { 71 int i, j; 72 73 if (pos <= 0 || pos >= MAXCOL) 74 return 0; 75 else { 76 i = 0; 77 for (j = pos; j < MAXCOL; j++,i++) { 78 line[i] = line[j]; 79 } 80 return i; 81 } 82 }
借助实例来理解程序会好一些,三个实例运行结果如下:
对实例①的分析如下:
实例②是函数exptab满足if(pos<MAXCOL)的情况,实例③是函数exptab满足else的情况。
还有一点是我觉得函数findblnk应该在前面加入pos--,比如上面的实例①中,pos=10的时候执行else if (++pos >= MAXCOL),但此时c被存入line[9],line[10]是空的。
1-23
自己写了一个,但是处理中间有*的程序诸如1-13.c在*会出错,这也是程序不完美的地方。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 #define MAXLINE 1000 3 4 int getline(char line[], int maxline, int pos); 5 6 /* 16/06/15 删除C程序中所有的注释语句,但不完美 */ 7 main() 8 { 9 int len; 10 int i = 0, j = 0; 11 char line_dat[MAXLINE]; 12 int position = 0; 13 14 /* 下面的代码段将输入文本的内容保存在同一个数组line_dat里面,回车用\n取代 */ 15 while ((len = getline(line_dat, MAXLINE, position)) >0) { 16 position = len; 17 } 18 line_dat[position] = '\0'; //整个文本输入完毕后,在保存数据的字组line_dat末尾加入\0表征字符串的结尾 19 i = 0; 20 printf("%s\n", line_dat); 21 } 22 23 int getline(char s[], int lim, int pos) 24 { 25 int c, i; 26 for (i = pos; i < lim - 1 && (c = getchar()) != EOF&&c != '\n'; ++i) { 27 if (c == '/') { 28 c = getchar(); 29 if (c == '*') { 30 while ((c = getchar()) != '*'); //一直读到下一个*为止 31 if ((c = getchar()) == '/') break; //如果*的下一个字符是/,说明是/* */注释,可以删除 32 else { 33 s[i++] = '*'; //如果*的下一个不是/,说明是其他用途比如乘号运算,需要保留 34 s[i] = c; 35 } 36 } 37 else if (c == '/') { 38 while ((c = getchar()) != '\n'); 39 break; 40 } 41 else { //除法运算需要保留/ 42 s[i++] = '/'; 43 s[i] = c; 44 } 45 } 46 else s[i] = c; 47 } 48 if (c == EOF) return -1; 49 if (c == '\n') 50 s[i++] = c; 51 return i; 52 }
下面是1-13.c源代码,
处理结果如下
答案的程序大致看了一遍,确实是very good!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 3 void rcomment(int c); 4 void in_comment(void); 5 void echo_quote(int c); 6 7 /* 16/06/20 答案写的更好更清晰,但是不能删除//格式的注释,且是以行为单位输出 */ 8 main() 9 { 10 int c; 11 12 while ((c = getchar()) != EOF) 13 rcomment(c); 14 return 0; 15 } 16 17 void rcomment(int c) 18 { 19 int d; 20 21 if (c == '/') { 22 if ((d = getchar()) == '*') 23 in_comment(); //如果是*/的话,就调用函数in_comment搜索注释语句的结束标志*/ 24 else if (d == '/') { //也就是说//格式的注释不会被删除 25 putchar(c); 26 rcomment(d); //这是函数嵌套调用 27 } 28 else { //符号/后面如果不是*的话说明不是注释内容 29 putchar(c); 30 putchar(d); 31 } 32 } 33 else if (c == '\'' || c == '"') //还考虑到了单引号和双引号的情况 34 echo_quote(c); 35 else 36 putchar(c); 37 } 38 39 void in_comment(void) 40 { 41 int c, d; //设立两个字符型变量,一次性存入两个字符,确实妙,我应该想到这一点 42 c = getchar(); 43 d = getchar(); 44 while (c != '*' || d != '/') { 45 c = d; 46 d = getchar(); 47 } 48 49 } 50 51 void echo_quote(int c) 52 { 53 int d; 54 putchar(c); 55 while ((d = getchar()) != c) { //查找单/双引号的另一边 56 putchar(d); 57 if (d == '\\') 58 putchar(getchar()); //不会把反斜号后面的引号看作是结束引号 59 } 60 putchar(d); 61 }
运行结果如下: