第1章-导言-习题1.18-1.23

1-18

思路是改动getline函数,读入某个输入行后,从该输入行尾部开始往前读,一直读到非空格或非制表符为止。

 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 }
1-18-2.c

答案也是采取了从尾部往前读一直到非空格/非制表符为止,不过仍只能处理单行输入的情况,不如我的程序好,而且不像我的程序那样使用void count(char s[])来检验,无法直观感觉到底删除没有。

 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 }
1-18-a.c

加入void count(char s[]),输出结果如下

从运行结果得知,这一程序并没有删除完全是空格的行,而只是不显示而已。

 

1-19

 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 }
1-19-2.c

答案和我的函数reverse不同之处只在于交换赋值的方法不同而已,实际运行效果相同。

 

1-20(和1-21一起看,做后面第5章的习题5-11和5-12需要回头看一下这里)

当时没看明白习题说的是什么意思,直接看的答案。

 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 }
1-20-a.c

运行结果如下(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

 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 }
1-21-a.c

输出结果(注意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

先自己写了一个,

 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 }
1-22.c

输出结果如下,每行的折合数为10个字符

从答案来看,我原来写程序时把练习理解简单了。答案程序能够完成的功能包括:把制表符扩展为空格;每遇到一个换行符就将此前的输入文本打印出来;每当变量pos的值达到MAXCOL时,就会对输入行进行折叠

 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 }
1-22-a.c

借助实例来理解程序会好一些,三个实例运行结果如下:

对实例①的分析如下:

实例②是函数exptab满足if(pos<MAXCOL)的情况,实例③是函数exptab满足else的情况。
还有一点是我觉得函数findblnk应该在前面加入pos--,比如上面的实例①中,pos=10的时候执行else if (++pos >= MAXCOL),但此时c被存入line[9],line[10]是空的。

 

1-23

自己写了一个,但是处理中间有*的程序诸如1-13.c在*会出错,这也是程序不完美的地方。

 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-23.c

下面是1-13.c源代码,

处理结果如下

答案的程序大致看了一遍,确实是very good!

 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 }
1-23-a.c

运行结果如下:

 

 

posted @ 2016-08-03 10:59  cust渔舟唱晚  阅读(122)  评论(0编辑  收藏  举报