练习 1-8 编写一个将输入复制到输出的程序,并将其中连续的空格用一个空格表示。
#include <stdio.h> #define ON 1 #define OFF 0 int main() { int c, button; button = OFF; c = getchar(); while(c != EOF) { if (c != ' ') { putchar(c); button = OFF; } else { if (button == OFF) { button = ON; putchar(c); } } c = getchar(); } return 0; }
练习 1-12 编写一个程序, 以每行一个单词的形式打印其输入。
思路1: 对单词进行操作
/*
* Step 1: devide characters into two types, e.g.
* hello world ni hao
* +++++-----+++++---++----+++
* Step 2: use curc marking the current char,
* prec marking the previous char.
* we could get four arrangements: ++, +-, -+, --
* Step 3: sequence of "+-" is what we need
*/
#include <stdio.h>
#define IN 1
#define OUT 0
int main()
{
int c, status;
int prec, curc;
status = OUT;
prec = curc = getchar();
while (curc != EOF) {
if (curc != ' ' && curc != '\n' && curc != '\t') {
status = IN;
putchar(curc);
} else
status = OUT;
if (status == OUT)
if (prec != ' ' && prec != '\n' && prec != '\t')
putchar('\n');
prec = curc;
curc = getchar();
}
return 0;
}
思路2: 对空格进行操作
#include <stdio.h> #define IN 1 #define OUT 0 int main() { int c, status; status = OUT; c = getchar(); while (c != EOF) { if (c == ' ' || c == '\t' || c == '\n') { if (status == IN) putchar('\n'); status = OUT; } else { status = IN; putchar(c); } c = getchar(); } return 0; }
思考一下下面表达式分别用在什么情况?
c != ' ' && c != '\n' && c != '\t'
c == ' ' || c == '\t' || c == '\n'
c != ' ' || c != '\t' || c != '\n'
练习 1-13 编写一个程序,打印输入中单词长度的直方图。
无论水平直方图还是垂直直方图,对单词统计的部分是一样的,不同之处在于显示。
统计部分的思路:
所有的字符分成两类,单词和空格。
遇到单词的时候,进行计数; 遇到空格的时候,计数保存,清零。
水平直方图:
显示部分需要注意的是: 先找出最长的一项,其余长度在它基础上进行比例放缩,得到的比例因子再与标准长度相乘。
之所以要进行这样处理,在于直方图要显示出每一项的相对关系,因此需要一个统一的基准。
#include <stdio.h> #define MAXHIST 15 #define MAXWORD 11 #define IN 1 #define OUT 0 int main() { int i, c, nc; int wl[MAXWORD]; int overflow; int status; int len; int maxvalue; for (i = 0; i < MAXWORD; ++i) wl[i] = 0; status = OUT; nc = 0; overflow = 0; while ((c = getchar()) != EOF) { if (c == ' ' || c == '\n' || c == '\t') { status = OUT; if (nc > 0) if (nc < MAXWORD) ++wl[nc]; else ++overflow; nc = 0; } else if (status == OUT) { status = IN; nc = 1; } else ++nc; } maxvalue = 0; for (i = 0; i < MAXWORD; ++i) if (wl[i] > maxvalue) maxvalue = wl[i]; len = 0; for (i = 1; i < MAXWORD; ++i) { printf("%5d - %5d : ", i, wl[i]); if (wl[i] > 0) { len = wl[i] * MAXHIST / maxvalue; if (len <= 1) len = 1; } else len = 0; while (len > 0) { putchar('*'); --len; } putchar('\n'); } if (overflow > 0) printf("There are %d words >= %d\n", overflow, MAXWORD); return 0; }
垂直直方图:
垂直显示的思路是:
1)假设显示分成两部分,垂直方向和水平方向
2)垂直方向,依次列出所有可能出现的长度
3)水平方向,假想图中已经出现每一列的直方图,
现在开始依次逐行扫描, 打点
#include <stdio.h> #define IN 1 #define OUT 0 #define MAXWORD 11 #define MAXVERT 15 int main() { int c, status, nc, overflow, i, j; int maxvalue; int wl[MAXWORD]; for (i = 0; i < MAXWORD; ++i) wl[i] = 0; nc = 0; overflow = 0; status = OUT; while ((c = getchar()) != EOF) { if (c == ' ' || c == '\n' || c == '\t') { status = OUT; if (nc > 0) if (nc < MAXWORD) ++wl[nc]; else ++overflow; nc = 0; } else if (status == OUT) { status = IN; nc = 1; } else ++nc; } maxvalue = 0; for (i = 1; i < MAXWORD; ++i) { if (wl[i] > maxvalue) maxvalue = wl[i]; } for (i = MAXVERT; i > 0; --i) { for (j = 1; j < MAXWORD; ++j) { if (wl[j] * MAXVERT / maxvalue >= i) printf(" *"); else printf(" "); } putchar('\n'); } for (i = 1; i < MAXWORD; ++i) printf("%4d", i); putchar('\n'); for (i = 1; i < MAXWORD; ++i) printf("%4d", wl[i]); putchar('\n'); if (overflow > 0) printf("There are %d words >= %d\n", overflow, MAXWORD); }
练习 1-20 编写detab, 将输入中的制表符换成适当数目的空格。
/* * detab.c * step 1: devide chars into 3 types: EOF, \t, \n * step 2: suppose a column with 8 chars width like this: |12345678| * if we use pos represents the current position in a line, * pos%TABINC will stand for left length in a column(e.g. pos is 3, LeftLength is 3%8=3), and * TABINC-pos%TABINC will represent right length in a column(e.g. RightLength is 8-3%8=5) * step 3: when we encounter \t, if we want to replace \t to spaces, * we need calculate the left length and right length of former chars */ #include <stdio.h> #define TABINC 8 int main() { char c; int pos, nb; pos = 1; nb = 0; while ((c = getchar()) != EOF) { if (c == '\t') { nb = TABINC - (pos - 1) % TABINC; while (nb > 0) { putchar(' '); --nb; ++pos; } } else if (c == '\n') { putchar(c); pos = 1; } else { putchar(c); ++pos; } } }
练习 1-21 编写程序entab, 将空格替换为数量最少的制表符和空格,但要保持单词之间的间隔不变。 当使用一个制表符或着一个空格符号都可以达到下一个制表符终止位置时, 选用哪种替换字符比较好?
审题时候要注意: 哪个数量要保证最少? 这里应该是制表符和空格总和达到最少。如果理解成制表符最少,编写思路是不同的。
后面一个问题值得注意,因为这关系到编写思路。 简单分析一下:
1)如果使用 \t 达到终止位置, 此时需要1个制表符
2)如果使用 达到最终位置, 此时需要1个空格
单单考虑最后一个位置的元素,是没有区别的。但如果考虑前面位置的元素,例如出现空格,空格,空格, 或者空格,制表符,空格等等情况
此时,1)的优势就出来了,前面的若干空格可用一个制表符来替代,同时要注意清空那些被替换的空格!
综上所述, 应该是选用制表符, 可以压缩制表符和空格的总和。
#include <stdio.h> #define TABINC 8 int main() { char c; int pos, nb,nt; pos = 1; nb = 0; nt = 0; while ((c = getchar()) != EOF) { if (c == ' ') { if (pos % TABINC != 0) ++nb; else { nb = 0; ++nt; } } else { for (; nt > 0; --nt) putchar('\t'); if (c == '\t') nb = 0; while (nb > 0) { putchar(' '); --nb; } putchar(c); if (c == '\n') pos = 0; else if (c == '\t') pos = pos + (TABINC - (TABINC - 1) % TABINC) - 1; } ++pos; } return 0; }
1-22 编写一个程序,把较长的行折成短一些的行,折行的位置在输入行第 n 列之前的最后一个非空格符之后。 要保证程序能智能地处理输入行很长,以及在制定的列前没有空格和制表符的情况。
#include <stdio.h> #define MAXCOL 10 #define TABINC 8 char line[MAXCOL]; void printl(int pos); int exptab(int pos); int findblank(int pos); int newpos(int pos); int main() { char c; int pos; pos = 0; while ((c = getchar()) != EOF) { line[pos] = c; if (c == '\t') pos = exptab(pos); else if (c == '\n') { printl(pos); pos = 0; } else if (++pos >= MAXCOL) { // why ++pos ? because the start pos of line[] is 0, whereas TAB counts from 1 pos = findblank(pos); printl(pos); pos = newpos(pos); } } return 0; } void printl(int pos) { int i; for (i = 0; i < pos; ++i) putchar(line[i]); if (pos > 0) putchar('\n'); } int exptab(int pos) { line[pos] = ' '; for (++pos; pos < MAXCOL && pos % TABINC != 0; ++pos) line[pos] = ' '; if (pos < MAXCOL) return pos; else { printl(pos); return 0; } } int findblank(int pos) { while (pos > 0 && line[pos] != ' ') --pos; if (pos == 0) return MAXCOL; else return pos + 1; } int newpos(int pos) { int i; if (pos <= 0 && pos >= MAXCOL) return 0; else { i = 0; while (pos < MAXCOL) { line[i] = line[pos]; ++i; ++pos; } return i; } }
1-23 编写一个删除C语言中所有注释的程序。要正确处理带引号和字符常量。在C语言中,注释不允许嵌套。
注意点: 引号有两种,单引号和双引号。 注释符号可能会出现在引号中。
所谓的字符常量就是带反斜杠进行转义的字符。 尤其要注意 \' 和 \" , 不能把反斜杠后面的引号作为引号结束符 !
#include <stdio.h> void rcomment(int c); void long_comment(void); void short_comment(void); void echo_quote(int c); int main() { int c; while ((c = getchar()) != EOF) { rcomment(c); } return 0; } void rcomment(int c) { int d; if (c == '/') { if ((d = getchar()) == '*') long_comment(); else if (d == '/') short_comment(); else { putchar(c); putchar(d); } } else if (c == '\'' || c == '"') echo_quote(c); else putchar(c); } void long_comment(void) { int c, d; c = getchar(); d = getchar(); while(c != '*' || d != '/') { c = d; d = getchar(); } } void short_comment(void) { int c; while((c = getchar()) != '\n') c = getchar(); putchar('\n'); } void echo_quote(int c) { int d; putchar(c); while((d = getchar()) != c) { putchar(d); if (d == '\\') putchar(getchar()); } putchar(c); }
1-24 编写一个程序,查找C语言中基本的语法错误,如圆括号,方括号, 花括号不配对等。要真却处理引号(包括单引号,双引号), 转义字符, 与注释。
注意: 本程序只对3种括号的数目匹配进行检查。
要考虑括号出现在注释中怎么办
要考虑括号出现在引号中怎么办
#include <stdio.h> int brace, brack, paren; void search(int c); void long_comment(void); void short_comment(void); void in_quote(int c); int main() { int c, d; extern int brace, brack, paren; while ((c = getchar()) != EOF) { if (c == '/') { if ((d = getchar()) == '*') long_comment(); else if (d == '/') short_comment(); else search(d); } else if (c == '\'' || c == '"') in_quote(c); else search(c); if (brace < 0) { printf("Unbalanced braces\n"); brace = 0; } else if (brack < 0) { printf("Unbalanced brackets\n"); brack = 0; } else if (paren < 0) { printf("Unbalanced parentheses\n"); paren = 0; } } if (brace > 0) printf("Unbalanced braces\n"); if (brack > 0) printf("Unbalanced brackets\n"); if (paren > 0) printf("Unbalanced parentheses\n"); return 0; } void search(int c) { extern int brace, brack, paren; if (c == '{') ++brace; else if (c == '[') ++brack; else if (c == '(') ++paren; else if (c == '}') --brace; else if (c == ']') --brack; else if (c == ')') --paren; } void long_comment(void) { int c, d; c = getchar(); d = getchar(); while (c != '*' || d != '/') { c = d; d = getchar(); } } void short_comment(void) { int c; while ((c = getchar()) != '\n') c = getchar(); } void in_quote(int c) { int d; while ((d = getchar()) != c) if (d == '\\') getchar(); }
主程序中对括号出错清零是为了下次括号出错做准备。
主程序中只对括号出现负数打印,因为如果为正数,括号的另一半可能会在后面语句中。