1-13
先放上自己写的程序,不论是打印水平方向的直方图还是垂直方向的直方图,前提都是先得到单词的长度,然后将该长度在数组nlenth[]对应的元素加1,也就说统计不同长度出现的次数。
1 #include<stdio.h> 2 3 #define IN 1 4 #define OUT 0 5 #define MAXSIZE 10 6 7 /* 16/06/09 打印输入中单词长度的直方图 */ 8 main() 9 { 10 char state = OUT; 11 char c,i; 12 int nlength[MAXSIZE]; 13 char nword = 0; 14 15 for (i = 0; i < MAXSIZE; i++) { 16 nlength[i] = 0; 17 } 18 while ((c = getchar()) != EOF) { 19 if (c == ' ' || c == '\t' || c == '\n') { 20 state = OUT; 21 } 22 else if (state == OUT) { 23 state = IN; 24 } 25 while (state == IN) { 26 nword++; 27 c = getchar(); 28 if (c == ' ' || c == '\t' || c == '\n') { 29 state = OUT; 30 nlength[nword]++; 31 nword = 0; 32 } 33 } 34 } 35 /* 绘制水平方向直方图 */ 36 /* 37 for (i = 0; i < MAXSIZE; i++) { 38 printf("%d ", i); 39 for (j = 0; j < nlength[i]; j++) { 40 putchar('*'); 41 } 42 putchar('\n'); 43 } 44 */ 45 /* 绘制垂直方向直方图,思路仍是一行行输出 */ 46 int ncopy[MAXSIZE]; 47 int ln, k, kmax; 48 /* 下面这一段代码是求数组nlength[]中的最大值 */ 49 for (ln = sizeof(nlength) / sizeof(int), k = kmax = 0; k < ln; k++) { 50 if (nlength[kmax] < nlength[k]) 51 kmax = k; 52 } 53 for (i = 0; i < MAXSIZE; i++) { 54 ncopy[i] = nlength[i]; 55 printf("%d ", i); 56 } 57 putchar('\n'); 58 for (k = 0; k < kmax; k++) { 59 for (i = 0; i < MAXSIZE; i++) { 60 if (ncopy[i] > 0) { 61 putchar('*'); 62 putchar(' '); 63 ncopy[i]--; 64 } 65 else { 66 putchar(' '); 67 putchar(' '); 68 } 69 } 70 putchar('\n'); 71 } 72 73 }
打印水平方向的直方图容易点,运行结果如下(注意输入完毕后先Enter再Ctrl+Z)
接下来是绘制垂直方向的直方图,写的稍微费事了一些,思路是先找到数组nlength[]中的最大值,在一次性输出0-9的过程中将数组nlength复制到另一个数组ncopy(否则后面的自减会丢失),然后根据相应数目输出*。
答案的前提和我的相同,也需要统计不同长度出现的次数,不过引入了overflow来统计超出允许最大长度的单词个数。
对于水平直方图(程序为1-13-a1.c),答案额外写了一段代码来找到统计到的长度数值中的最大值,以此来计算wl[i]对应的直方图长度(我的是有多少就输出多少*),计算公式是len=wl[i]*MAXHIST/maxvalue,我觉得采取这个公式只是起到计算的作用,没有特殊意义。
1 #include<stdio.h>
2
3 #define IN 1
4 #define OUT 0
5 #define MAXSIZE 10
6 #define MAXHIST 15
7
8 /* 16/06/15 打印输入单词长度的水平直方图 */
9 main()
10 {
11 char state = OUT;
12 char c, i;
13 int nlength[MAXSIZE];
14 int len;
15 int nc, overflow;
16 int maxvalue;
17
18 nc = 0;
19 overflow = 0;
20 for (i = 0; i < MAXSIZE; i++) {
21 nlength[i] = 0;
22 }
23 while ((c = getchar()) != EOF) {
24 if (c == ' ' || c == '\t' || c == '\n') {
25 state = OUT;
26 if (nc > 0) {
27 if (nc<MAXSIZE)
28 ++nlength[nc];
29 else ++overflow;
30 }
31 nc = 0;
32 }
33 else if (state == OUT) {
34 state = IN;
35 nc = 1;
36 }
37 else nc++;
38 }
39 /* 绘制水平方向直方图 */
40 maxvalue = 0;
41 for (i = 1; i < MAXSIZE; i++) {
42 if (maxvalue < nlength[i])
43 maxvalue = nlength[i];
44 }
45 for (i = 1; i < MAXSIZE; i++) {
46 printf("%d - %d : ", i, nlength[i]);
47 if (nlength[i] > 0) {
48 if ((len = nlength[i] * MAXHIST / maxvalue) <= 0)
49 len = 1;
50 }
51 else len = 0;
52 while (len--)
53 putchar('*');
54 putchar('\n');
55 }
56 if (overflow > 0)
57 printf("There are %d words >= %d\n", overflow, MAXSIZE);
58
59 }
运行效果如下:
1-13-a2是绘制垂直直方图,
1 #include<stdio.h>
2
3 #define IN 1
4 #define OUT 0
5 #define MAXSIZE 10
6 #define MAXHIST 15
7
8 /* 16/06/15 打印输入单词长度的垂直直方图 */
9 main()
10 {
11 char state = OUT;
12 char c, i, j;
13 int nlength[MAXSIZE];
14 int nc, overflow;
15 int maxvalue;
16
17 nc = 0;
18 overflow = 0;
19 for (i = 0; i < MAXSIZE; i++) {
20 nlength[i] = 0;
21 }
22 while ((c = getchar()) != EOF) {
23 if (c == ' ' || c == '\t' || c == '\n') {
24 state = OUT;
25 if (nc > 0) {
26 if (nc<MAXSIZE)
27 ++nlength[nc];
28 else ++overflow;
29 }
30 nc = 0;
31 }
32 else if (state == OUT) {
33 state = IN;
34 nc = 1;
35 }
36 else nc++;
37 }
38 /* 绘制水平方向直方图 */
39 maxvalue = 0;
40 for (i = 1; i < MAXSIZE; i++) {
41 if (maxvalue < nlength[i])
42 maxvalue = nlength[i];
43 }
44 for (i = MAXHIST; i > 0; i--) { //之所以是从MAXHIST开始是为了从底部开始显示*,看一下运行结果就知道了
45 for (j = 1; j < MAXSIZE; j++) {
46 if ((nlength[j] * MAXHIST / maxvalue) >= i)
47 printf(" * ");
48 else
49 printf(" ");
50
51 }
52 putchar('\n'); //注意这一句是必须写的
53 }
54 for (i = 1; i < MAXSIZE; i++)
55 printf(" %d ", i);
56 putchar('\n');
57 for (i = 1; i < MAXSIZE; i++)
58 printf(" %d ", nlength[i]);
59 putchar('\n');
60 if (overflow > 0)
61 printf("There are %d words >= %d\n", overflow, MAXSIZE);
62
63 }
运行效果如下,
1-14
我仅仅是在教材P15例程基础上稍作改动,根据统计到的ndigit[i]、nwhite、nother打印相应数目的*,
1 #include<stdio.h> 2 3 /* 16/06/09 打印输入中各个字符出现频度的直方图 */ 4 main() 5 { 6 char c, i, j,nwhite, nother; 7 int ndigit[10]; 8 9 nwhite = nother = 0; 10 for (i = 0; i < 10; i++) { 11 ndigit[i] = 0; 12 } 13 while ((c = getchar()) != EOF) { 14 if (c >= '0'&&c <= '9') 15 ++ndigit[c - '0']; 16 else if (c == ' ' || c == '\t' || c == '\n') 17 ++nwhite; 18 else ++nother; 19 } 20 /* 绘制水平方向直方图 */ 21 for (i = 0; i < 10; i++) { 22 printf("%d ", i); 23 for (j = 0; j < ndigit[i]; j++) { 24 putchar('*'); 25 } 26 putchar('\n'); 27 } 28 29 printf("white "); 30 for (i = 0; i < nwhite; i++) putchar('*'); 31 putchar('\n'); 32 33 printf("other "); 34 for (i = 0; i < nother; i++) putchar('*'); 35 putchar('\n'); 36 37 }
答案能够处理的字符是ASCII字符集范围内的字符,因而设置了#define MAXCHAR 128,在这个宏定义的范围内,++cc[c]。
1 #include<stdio.h>
2 #include<ctype.h>
3
4 #define MAXCHAR 128
5 #define MAXHIST 15
6
7 /* 16/06/15 打印输入中各个字符出现频度的直方图 */
8 main()
9 {
10 int c, i;
11 int len;
12 int maxvalue;
13 int cc[MAXCHAR];
14
15 for (i = 1; i < MAXCHAR; i++) {
16 cc[i] = 0;
17 }
18 while ((c = getchar()) != EOF) {
19 if (c < MAXCHAR) {
20 ++cc[c];
21 }
22 }
23 /* 绘制水平方向直方图 */
24 maxvalue = 0;
25 for (i = 1; i < MAXCHAR; i++) {
26 if (maxvalue < cc[i])
27 maxvalue = cc[i];
28 }
29 for (i = 1; i < MAXCHAR; i++) {
30 if (isprint(i))
31 printf("%5d - %c - %5d : ", i, i, cc[i]);
32 else
33 printf("%5d - - %5d : ", i, cc[i]);
34 len = cc[i]; //还是直接让len=cc[i],好理解一些,下面的代码也只是输出cc[i]的倍数而已
35 /*
36 if (cc[i] > 0) {
37 if ((len = cc[i] * MAXCHAR / maxvalue) <= 0)
38 len = 1;
39 }
40 else len = 0;
41 */
42 while (len > 0) {
43 putchar('*');
44 --len;
45 }
46 putchar('\n');
47 }
48
49 }
注意后面的输出格式设置,如果是ASCII字符集范围内的字符就显示出来,否则打印空白,
1 if (isprint(i)) 2 printf("%5d - %c - %5d : ", i, i, cc[i]); 3 else 4 printf("%5d - - %5d : ", i, cc[i]);
运行结果如下
1-15
比较简单,但是我的程序比较糟糕的一点是在函数Fahr_to_Cel内使用了printf,感觉独立性差了点,
1 #include<stdio.h> 2 3 #define LOWER 0 4 #define UPPER 300 5 #define STEP 20 6 7 void Fahr_to_Cel(int lower, int upper, int step); 8 9 /* 16/06/09 温度转换程序 */ 10 main() 11 { 12 Fahr_to_Cel(LOWER, UPPER, STEP); 13 } 14 15 void Fahr_to_Cel(int lower, int upper, int step) 16 { 17 int fahr, celsius; 18 19 for (fahr = lower; fahr <= upper; fahr += step) { 20 celsius = 5 * (fahr - 32) / 9; 21 printf("%d\t%d\n", fahr, celsius); 22 } 23 }
下面给出参考答案程序,
1 #include<stdio.h> 2 3 #define LOWER 0 4 #define UPPER 300 5 #define STEP 20 6 7 float Fahr_to_Cel(float fahr); 8 9 /* 16/06/15 温度转换程序 */ 10 main() 11 { 12 float fahr; 13 for (fahr = LOWER; fahr < UPPER; fahr += STEP) { 14 printf("%3.0f %6.1f\n",fahr,Fahr_to_Cel(fahr)); 15 } 16 } 17 18 float Fahr_to_Cel(float fahr) 19 { 20 return (5.0 / 9.0)*(fahr - 32.0); 21 }
1-16
①开始思路是将某行的长度与该输入行放在同一数组line[]里面,然后再接下一行的长度和内容,
1 #include<stdio.h> 2 #define MAXLINE 1000 3 4 int getline(char line[], int maxline,int pos); 5 6 /* 16/06/09 程序的思路是将某行的长度与该行放在一起,后接下一行,存在很大缺陷,输入某个行的长度不能超过9 */ 7 main() 8 { 9 int c,len; 10 char line[MAXLINE]; 11 int position=0; 12 13 while((c=getchar())!=EOF){ 14 line[position + 2] = c; 15 len = getline(line, MAXLINE, position+3); 16 line[position] = (len-position-3)+'0'; 17 line[position+1] = ' '; 18 position = len; 19 } 20 line[position] = '\0'; 21 printf("%s", line); 22 } 23 24 int getline(char s[], int lim, int pos) 25 { 26 int c, i; 27 for (i = pos; i < lim - 1 && (c = getchar()) != EOF&&c != '\n'; ++i) 28 s[i] = c; 29 if (c == '\n') 30 s[i++] = c; 31 return i; 32 }
但是单个输入行的长度不要超过9,否则该输入行长度显示会出错,原因在于10以上的数字无法用单个ASCII字符表示。
②看来只能将长度与数据分开存放了,运行结果如下,重要的地方已在程序注释。
1 #include<stdio.h> 2 #define MAXLINE 1000 3 4 int getline(char line[], int maxline, int pos); 5 6 /* 16/06/09 程序的思路是将某行的长度与该行内容分开存放在不同数组line_num与line_dat */ 7 main() 8 { 9 int c, len; 10 int i = 0, j = 0; 11 char line_dat[MAXLINE]; 12 char line_num[MAXLINE]; 13 int position = 0; 14 15 /* 下面的代码段将输入文本的内容保存在同一个数组line_dat里面,回车用\n取代 */ 16 while ((c = getchar()) != EOF) { 17 line_dat[position] = c; //这一句是有必要的,否则头字母丢失 18 len = getline(line_dat, MAXLINE, position + 1); //从该行的第二个字母开始判断 19 line_num[i++] = len-position-1; //保存该行的长度 20 position = len; 21 } 22 line_dat[position] = '\0'; //整个文本输入完毕后,在保存数据的字组line_dat末尾加入\0表征字符串的结尾 23 i = 0; 24 /* 下面的代码段先输出某行的长度,再输出该行的内容 */ 25 while( i < MAXLINE&&line_dat[i] != '\0') { 26 printf("%d\t", line_num[j++]); 27 for (; line_dat[i] != '\n'; i++) { 28 putchar(line_dat[i]); 29 } 30 putchar(line_dat[i++]); 31 } 32 } 33 34 int getline(char s[], int lim, int pos) 35 { 36 int c, i; 37 for (i = pos; i < lim - 1 && (c = getchar()) != EOF&&c != '\n'; ++i) 38 s[i] = c; 39 if (c == '\n') 40 s[i++] = c; 41 return i; 42 }
③前面两个程序均是在主函数main()内部借助c = getchar()) != EOF来判断是否达到文本流结尾,其实完全可以直接借助getline函数来判断(如果到达文本流结尾就返回-1),这样会使程序简化一些。
1 #include<stdio.h> 2 #define MAXLINE 1000 3 4 int getline(char line[], int maxline, int pos); 5 6 /* 16/06/10 写完1-18-2,回头改写了1-16-2,运行正常 */ 7 main() 8 { 9 int len; 10 int i = 0, j = 0; 11 char line_dat[MAXLINE]; 12 char line_num[MAXLINE]; 13 int position = 0; 14 15 /* 下面的代码段将输入文本的内容保存在同一个数组line_dat里面,回车用\n取代 */ 16 while ((len = getline(line_dat, MAXLINE, position)) >0) { 17 line_num[i++] = len - position - 1; //保存该行的长度 18 position = len; 19 } 20 line_dat[position] = '\0'; //整个文本输入完毕后,在保存数据的字组line_dat末尾加入\0表征字符串的结尾 21 i = 0; 22 /* 下面的代码段先输出某行的长度,再输出该行的内容 */ 23 while (i < MAXLINE&&line_dat[i] != '\0') { 24 printf("%d\t", line_num[j++]); 25 for (; line_dat[i] != '\n'; i++) { 26 putchar(line_dat[i]); 27 } 28 putchar(line_dat[i++]); 29 } 30 } 31 32 int getline(char s[], int lim, int pos) 33 { 34 int c, i; 35 for (i = pos; i < lim - 1 && (c = getchar()) != EOF&&c != '\n'; ++i) 36 s[i] = c; 37 if (c == EOF) return -1; 38 if (c == '\n') 39 s[i++] = c; 40 return i; 41 }
答案主要在教材【P21】的例程基础上对函数getline进行了修改,删除了i<lim-1,并加入if(i<lim-2)条件判断。答案是以每个输入行为单位一行一行地输出,无法像我的程序那样处理多行。
1 #include<stdio.h> 2 #define MAXLINE 1000 3 4 int getline(char s[], int lim); 5 6 /* 16/06/15 是以每个输入行为单位一行一行地输出,无法像我的程序那样处理多行 */ 7 main() 8 { 9 int len; 10 int i = 0, j = 0; 11 char line_dat[MAXLINE]; 12 13 /* 下面的代码段将输入文本的内容保存在同一个数组line_dat里面,回车用\n取代 */ 14 while ((len = getline(line_dat, MAXLINE)) >0) { 15 printf("%d, %s", len, line_dat); 16 } 17 return 0; 18 } 19 20 int getline(char s[], int lim) 21 { 22 int c, i; 23 24 for (i = 0; (c = getchar()) != EOF && c != '\n'; ++i) { 25 if (i < lim - 2) { 26 s[i] = c; 27 } 28 } 29 if (c == '\n') { 30 s[i] = c; 31 i++; 32 } 33 s[i] = '\0'; 34 return i; 35 }
答案运行结果如下
注意对于超过1000的输入,运行结果如下(一行为120个字符,共10行),可以看出能输出正确数目,但是不会输出完整的尾部内容。
1-17
在1-16-2.c基础上稍作修改即可,这里是设定打印长度大于10个字符的所有输入行。
1 #include<stdio.h> 2 #define MAXLINE 1000 3 4 int getline(char line[], int maxline, int pos); 5 6 /* 16/06/09 打印长度大于10个字符的所有输入行,在1-16-2.c基础上修改 */ 7 main() 8 { 9 int c, len; 10 int i = 0, j = 0; 11 char line_dat[MAXLINE]; 12 char line_num[MAXLINE]; 13 int position = 0; 14 15 while ((c = getchar()) != EOF) { 16 line_dat[position] = c; 17 len = getline(line_dat, MAXLINE, position + 1); 18 line_num[i++] = len - position - 1; 19 position = len; 20 } 21 line_dat[position] = '\0'; 22 i = 0; 23 /* 下面是修改部分 */ 24 while (i < MAXLINE&&line_dat[i] != '\0') { 25 if (line_num[j]>10) { 26 printf("%d\t", line_num[j++]); 27 for (; line_dat[i] != '\n'; i++) { 28 putchar(line_dat[i]); 29 } 30 putchar(line_dat[i++]); 31 } 32 else { 33 j++; 34 while (line_dat[i++] != '\n'); 35 } 36 } 37 } 38 39 int getline(char s[], int lim, int pos) 40 { 41 int c, i; 42 for (i = pos; i < lim - 1 && (c = getchar()) != EOF&&c != '\n'; ++i) 43 s[i] = c; 44 if (c == '\n') 45 s[i++] = c; 46 return i; 47 }
答案是在1-16-a.c的基础上加入长度条件判断if(len>LONGLINE)即可。
1 #define LONGLIEN 80 2 ... 3 main(){ 4 ... 5 while ((len = getline(line_dat, MAXLINE)) >0) { 6 if(len>LONGLINE) 7 printf("%d, %s", len, line_dat); 8 } 9 }