小结
许多程序使用 getchar()逐字符读取输入。通常,系统使用行缓冲输入(用户输入的字符被收集并储存在一个被称为缓冲区(buffer)的临时存储区,按下Enter键后,程序才可使用用户输入的字符)。按下Enter键也传送了一个换行符,编程时要注意处理这个换行符。ANSI C把缓冲输入作为标准。
为什么要有缓冲区?
首先,把若干字符作为一个块进行传输比逐个发送这些字符节约时间。其次,如果用户打错字符,可以直接通过键盘修正错误。当最后按下Enter键时,传输的是正确的输入。
C 是一门强大、灵活的语言,有许多用于打开、读取、写入和关闭文件的库函数。
无论操作系统实际使用何种方法检测文件结尾,在C语言中,用getchar()读取文件检测到文件结尾时将返回一个特殊的值,即EOF(end offile的缩写)。scanf()函数检测到文件结尾时也返EOF。通常, EOF定义在stdio.h文件中:
#define EOF (-1)
使用该程序进行键盘输入,要设法输入EOF字符。正确的方法是,必须找出当前系统的要求。例如,在大多数UNIX和Linux系统中,在一行开始处按下Ctrl+D会传输文件结尾信号。许多微型计算机系统都把一行开始处的Ctrl+Z识别为文件结尾信号,一些系统把任意位置的Ctrl+Z解释成文件结尾信号。
可以用程序直接打开文件,待读取的文件应该与可执行文件位于同一目录。程序如下:
// file_eof.c --打开一个文件并显示该文件
#include<stdio.h>
#include<stdlib.h> // 为了使用exit()
int main()
{
int ch;
FILE * fp;
char fname[50];// 储存文件名
printf("Enter the name of the file: ");
scanf("%s", fname);
fp=fopen(fname, "r");
if(fp == NULL) //如果失败
{
printf("Failed to open file. Bye\n");
exit(1); // 退出程序
}
// getc(fp)从打开的文件中获取一个字符
while((ch = getc(fp)) != EOF)
putchar(ch);
fclose(fp); //关闭文件
return 0;
}
程序通常接受特殊形式的输入。可以在设计程序时考虑用户在输入时可能犯的错误,在输入验证部分处理这些错误情况,让程序更强健更友好。对于一个小型程序,输入验证可能是代码中最复杂的部分。处理这类问题有多种方案。例如,如果用户输入错误类型的信息,可以终止程序,也可以给用户提供有限次或无限次机会重新输入。
程序遵循模块化的编程思想,使用独立函数(模块)来验证输入和管理显示。程序越大,使用模块化编程就越重要。
复习题
复习题的参考答案在附录A中。
1.putchar(getchar())是一个有效表达式,它实现什么功能?
getchar(putchar())是否也是有效表达式?
表达式 putchar(getchar())使程序读取下一个输入字符并打印出来。getchar()的返回值是putchar()的参数。
但 getchar(putchar())是无效的表达式,因为getchar()不需要参数,而putchar()需要一个参数。
2.下面的语句分别完成什么任务?
a.putchar('H');
b.putchar('\007');
c.putchar('\n');
d.putchar('\b');
a.显示字符 H。
b.如果系统使用ASCII,则发出一声警报。
c.把光标移至下一行的开始。
d.退后一格。
3.假设有一个名为 count 的可执行程序,用于统计输入的字符数。设计一个使用 count 程序统计essay文件中字符数的命令行,并把统计结果保存在essayct文件中。
count<essay >essayct或者count >essayct<essay
4.给定复习题3中的程序和文件,下面哪一条是有效的命令?
a.essayct <essay
b.count essay
c.essay >count
都不是有效的命令。
5.EOF是什么?
EOF是由getchar()和scanf()返回的信号(一个特殊值),表明函数检测到文件结尾。
7.C如何处理不同计算机系统中的不同文件和换行约定?
C的标准 I/0库把不同的文件映射为统一的流来统一处理。
8.在使用缓冲输入的系统中,把数值和字符混合输入会遇到什么潜在的问题?
数值输入会跳过空格和换行符,但是字符输入不会。
编程练习
1.设计一个程序,统计在读到文件结尾之前读取的字符数。
/* Programming Exercise 8-1 */
#include <stdio.h>
int main(void)
{
int ch;
int ct = 0;
while ((ch = getchar()) != EOF)
ct++;
printf("%d characters read\n", ct);
return 0;
}
3.编写一个程序,在遇到 EOF 之前,把输入作为字符流读取。该程序要报告输入中的大写字母和小写字母的个数。假设大小写字母数值是连续的。或者使用ctype.h库中合适的分类函数更方便。
/* Programming Exercise 8-3 */
/* Using ctype.h eliminates need to assume consecutive coding */
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int ch;
unsigned long uct = 0;
unsigned long lct = 0;
unsigned long oct = 0;
while ((ch = getchar()) != EOF)
if (isupper(ch))
uct++;
else if (islower(ch))
lct++;
else
oct++;
printf("%lu uppercase characters read\n", uct);
printf("%lu lowercase characters read\n", lct);
printf("%lu other characters read\n", oct);
return 0;
}
/*
or you could use
if (ch >= 'A' && ch <= 'Z')
uct++;
else if (ch >= 'a' && ch <= 'z')
lct++;
else
oct++;
*/
5.修改程序清单8.4的猜数字程序,使用更智能的猜测策略。例如,程序最初猜50,询问用户是猜大了、猜小了还是猜对了。如果猜小了,那么下一次猜测的值应是50和100中值,也就是75。如果这次猜大了,那么下一次猜测的值应是50和75的中值,等等。使用二分查找(binary search)策略,如果用户没有欺骗程序,那么程序很快就会猜到正确的答案。
/* Programming Exercise 8-5 */
/* binaryguess.c -- an improved number-guesser */
/* but relies upon truthful, correct responses */
#include <stdio.h>
#include <ctype.h>
int main(void)
{
int high = 100;
int low = 1;
int guess = (high + low) / 2;
char response;
printf("Pick an integer from 1 to 100. I will try to guess ");
printf("it.\nRespond with a y if my guess is right, with");
printf("\na h if it is high, and with an l if it is low.\n");
printf("Uh...is your number %d?\n", guess);
while ((response = getchar()) != 'y') /* get response */
{
if (response == '\n')
continue;
if (response != 'h' && response != 'l')
{
printf("I don't understand that response. Please enter h for\n");
printf("high, l for low, or y for correct.\n");
continue;
}
if (response == 'h')
high = guess - 1;
else if (response == 'l')
low = guess + 1;
guess = (high + low) / 2;
printf("Well, then, is it %d?\n", guess);
}
printf("I knew I could do it!\n");
return 0;
}
7.修改第7章的编程练习8,用字符代替数字标记菜单的选项。用q代替5作为结束输入的标记。
/* Programming Exercise 8-7 */
#include <stdio.h>
#include <ctype.h>
#include <stdio.h>
#define BASEPAY1 8.75 // $8.75 per hour
#define BASEPAY2 9.33 // $9.33 per hour
#define BASEPAY3 10.00 // $10.00 per hour
#define BASEPAY4 11.20 // $11.20 per hour
#define BASEHRS 40 // hours at basepay
#define OVERTIME 1.5 // 1.5 time
#define AMT1 300 // 1st rate tier
#define AMT2 150 // 2st rate tier
#define RATE1 0.15 // rate for 1st tier
#define RATE2 0.20 // rate for 2nd tier
#define RATE3 0.25 // rate for 3rd tier
int getfirst(void);
void menu(void);
int main(void)
{
double hours;
double gross;
double net;
double taxes;
double pay;
char response;
menu();
while ((response = getfirst()) != 'q')
{
if (response == '\n') /* skip over newlines */
continue;
response = tolower(response); /* accept A as a, etc. */
switch (response)
{
case 'a': pay = BASEPAY1; break;
case 'b': pay = BASEPAY2; break;
case 'c': pay = BASEPAY3; break;
case 'd': pay = BASEPAY4; break;
default : printf("Please enter a, b, c, d, or q.\n");
menu();
continue; // go to beginning of loop
}
printf("Enter the number of hours worked this week: ");
scanf("%lf", &hours);
if (hours <= BASEHRS)
gross = hours * pay;
else
gross = BASEHRS * pay + (hours - BASEHRS) * pay * OVERTIME;
if (gross <= AMT1)
taxes = gross * RATE1;
else if (gross <= AMT1 + AMT2)
taxes = AMT1 * RATE1 + (gross - AMT1) * RATE2;
else
taxes = AMT1 * RATE1 + AMT2 * RATE2 + (gross - AMT1 - AMT2) * RATE3;
net = gross - taxes;
printf("gross: $%.2f; taxes: $%.2f; net: $%.2f\n", gross, taxes,net);
menu();
}
printf("Done.\n");
return 0;
}
void menu(void)
{
printf("********************************************************""*********\n");
printf("Enter the letter corresponding to the desired pay rate"" or action:\n");
printf("a) $%4.2f/hr b) $%4.2f/hr\n", BASEPAY1,BASEPAY2);
printf("c) $%5.2f/hr d) $%5.2f/hr\n", BASEPAY3,
BASEPAY4);
printf("q) quit\n");
printf("********************************************************""*********\n");
}
int getfirst(void)
{
int ch;
ch = getchar();
while (isspace(ch))
ch = getchar();
while (getchar() != '\n')
continue;
return ch;
}