第8章 字符输入/输出和输入验证

发布于 2021-05-04  594 次阅读


8.1 单字符I/O:getchar()和putchar()

//  echo.c  --重复输入
#include<stdio.h>
int main(void)
{
    char ch;
    while((ch=getchar())!='#')
        putchar(ch);
    return 0;
}

自从ANSI C标准发布以后,C就把头文件于使用getchar()和putchar()相关联。(它们其实不是真正的函数,被定义为供预处理器使用的宏)。

8.2 缓冲区

回显用户输入的字符后立即重复打印该字符是属于无缓冲(或直接)输入,即正在等待的程序可立即使用输入的字符。
对于该例,大部分系统在用户按下Enter键之前不会重复打印刚输入的字符,这种输入形式属于缓冲输入。用户输入的字符被收集并存储在了一个被称为缓冲区(buffer)的临时存储区,按下Enter后,程序才可使用用户输入的字符。
file
有了缓冲区:
(1)若干字符作为一个块进行传输比逐个发送这些字符节约时间。
(2)打错字符,可以通过键盘修正错误。当之后按下Enter时,传输的是正确的输入。
虽然缓冲输入好处很多,但是某些交互式程序也需要无缓冲。例如,游戏~
缓冲分为两类:完全缓冲I/O和行缓冲I/O完全缓冲输入指的是当缓冲区被填满时才刷新缓冲区(内容被发送至目的地),通常出现在文件输入中。缓冲区的大小取决于系统,常见的大小是512字节和4096字节。行缓冲I/O指的是再出现换行符时刷新缓冲区。键盘输入通常是行缓冲输入,所以在按下Enter后次啊刷新缓冲区。
ANSI C决定把缓冲输入作为标准是因为一些计算机不允许无缓冲输入。无缓冲:IBM PC 兼容机上conio.h头文件用于回显无缓冲getche()和无回显无缓冲getch().UNIX系统使用ioctl()函数然后getchar()..
在ANSI C中用setbuf()和setvbuf()函数控制缓冲,但是受限于一些系统内部设置,这些函数可能不起作用。总之,ANSI没有提供调用无缓冲输入的标准方式,这意味着能否进行无缓冲输入取决于计算机系统。

8.3 结束键盘键入

因为#也不可避免的可能被使用,C提供了一个文本中使用不到的字符。

8.3.1 文件、流和键盘输入

文件(file)是存储器中存储信息的区域。
C是一门强大、灵活的语言,有许多用于打开、读取、写入和关闭文件的库函数。从较低层面上,C可以使用主机操作系统的基本文件工具直接处理文件,这些调用操作系统的函数被称为底层I/O(low-level I/O)。由于计算机系统不同,所以不可能为普通的底层I/O函数创建标准库,ANSI C也不打算这样做。在较高层面上,C还可以通过标准的I/O包(standard I/O package)来处理文件。这涉及创建用于处理文件的标准模型和一套标准的I/O函数。具体的C实现负责处理不同系统的差异,以便用户使用统一的界面。
不同的系统存储文件的方式不同,有些系统把文件的内容存储在一处,而文件相关的信息存储在另外一处;有些系统在文件徐鸿创建一份文件描述。在处理文件方面,有些系统使用单个换行符记行末尾,而其他系统可能使用回车和换行符的组合来表示末尾。有些系统使用最小字节来衡量文件的大小,有些系统则以字节块的大小来衡量。
如果使用标准的I/O包,就不用考虑这些差异。因此,可以使用if(ch == '\n')检查换行符。即使系统实际用回车符和换行符的组合来标记行末尾,I/O函数会在两种表示法之间相互转换。
从概念上看,C程序处理的是流而不是直接处理文件。流(stream)是一个实际输入或输出映射的理想化数据流。这意味着不同属性和不同种类的输入,由属性更统一的流来表示。打开文件的过程就是把流与文件相关联,而且读写都通过流来完成。
本章着重理解C把输入和输出设备视为存储设备上的普通文件,尤其是把键盘和显示视为每个C程序自动打开的文件。stdin流表键盘输入,stdout流表示屏幕输出。getchar()、putchar()、printf()和scanf()函数都是标准I/O包的成员,处理这两个流。
以上讨论的内容说明,可以用处理文件的方式处理的方式来处理键盘的输入。例如,程序读文件时要能检测文件的末尾才知道应在何处停止。因此,C的输入函数内置了文件结尾检测器。既然可以把键盘输入视为文件,那么也应该能使用文件结尾检测器结束键盘的输入。

8.3.2 文件结尾

计算机操作系统要以某种方式判断文件的开始和结束。
检测文件结尾的一种方法是,再文件末尾放一个特殊的字符标记文件结尾。。
如今这些操作系统可以使用内嵌的ctrl+Z字符来标记文件结尾。
另外一种办法是存储文件大小信息。
无论操作系统实际使用哪种方法检测文件结尾,在C语言中,用getchar()读取文件检测到文件尾部时候将返回一个特殊的值,即EOF(end of file)。scanf()函数检测到文件结尾时也返回EOF。
通常EOF定义在stdio.h文件中:
#define EOF (-1)
为什么是-1,因为getchar()函数的返回值通常都介于0-127.拓展也是0-255故-1不占用任何一个。
EOF是一个值,标志着检测到文件结尾,并不是在文件中找得到的符号。
如何在程序中使用EOF?
答:把getcahr()的返回值和EOF作比较。如果两值不同,就说明没有到达文件结尾。也就是说:
while((ch=getchar())!=EOF)
#include<stdio.h>
int main(void)
{
    int ch;

    while((ch=getchar())!=EOF)
        putchar(ch);

    return 0;
 } 
- 不用定义EOF(stdio.h中包含)
- 不用担心EOF的实际值,因为在EOF在stdio.h中用#define预处理指令定义,可以直接使用,不必在编写代码假定EOF为某值。
- 变量ch的类型从char变为int,因为char类型的变量只能表示0-255的无符号整数,但是EOF的值是-1.还好,getchar()函数返回值的类型是int,所以它可以读取EOF字符。如果实现有符号的char类型,也可以把ch声明为char类型。
- 由于getchar()函数的返回值类型是int,如果把getchar()返回值赋给char类型的变量,一些编译器会警告可能丢失数据。
- ch是整数不会影响putchar(),该函数仍然会打印等价的字符。

8.4 重定向和文件

程序可以通过两种方式使用文件。
第一种是,显示使用特定的函数打开文件、关闭文件、读取文件、写入文件。
第二种是,设计能与键盘和屏幕互动的程序,通过不同渠道重新定像输入至文件和从文件输出。

8.4.1 UNIX\LINUX和DOS重定向

重定向可以让程序使用文件而不是键盘来输入,重定向让程序输出至文件而不是屏幕。

1.重定向输入

将已经编译好的echo_eof.c程序的可执行程序放入echo_eif名的文件夹。
运行该程序,输入可执行文件名:echo_eof
用该程序处名为words的文本文件:
echo_eof < words
<是unix和dos/windows的重定向运算符。
该运算符使words文件与stdin流相关联,把文件中的内容导入echo_eof程序。

2.重定向输出

现在假设要用echo_eof把键盘输入的内容发送到名为mywords的文件中。然后,输入以下命令:
echo_eof>mywords
>符号是第2个重定向运算符,它创建了一个名为myword的新文件,然后把echo_eof的纯输出重定向至该文件中。重定向把stdout从显示设备赋给了myword文件。如果已经有一个mywords的文件,通常会擦除该文件的内容,然后替换成新的内容。

3.组合重定向

现在,假设你希望制作一份mywords文件的副本,并且命名为savewords:
./echo_eof < mywords >savewords
下面的命令也起作用,因为命令与重定向运算符的顺序无关:
./echo_eof > savewords< mywords
注意:在一条命令中,输入文件名和输出文件名不能相同。
./echo_eof< mywords > mywords
原因是>mywords在输入之前已导致原mywords的长度被截断为0.
在OS中使用两个重定向运算符,遵循以下原则。
- 1.重定向运算符连接一个可执行程序(包括标准操作系统命令)和一个数据文件,不能用于连接一个数据文件和另一个数据文件,也不能用于连接一个程序和另一个程序。
- 2.使用重定向运算符不能读取多个文件的输入,也不能把输出定向至多个文件。
- 3.文件名和运算符之间的空格不是必须的,除非是偶尔在UNIX shell、linux shell或windows 命令行提示中使用的有特殊含义的字符。

UNIX、Linux或windows/DOS还有>>运算符,该运算符可以把数据添加到现有文件的末尾,而|运算符能把一个文件的输出连接到另一个文件的输入。

4.注释

重定位可以让从键盘输入程序文件。要完成这一任务,程序要能测试文件的末尾。
重定向是一个命令行概念,因为我门要在命令行中输入特殊的符号发出指令。如果不使用命令行环境,也可以使用重定向。一些继承开发环境提供了菜单选项,让用户指定重定向。对于windows系统,可以打开命令提示窗口,在命令提示窗口运行可执行文件。VS的默认设置是把可执行文件放在项目文件夹的子文件夹,称为Debug。文件名和项目名基本相同,文件名的扩展名为.exe.
默认情况下,Xcode在给项目命名后才能命名可执行文件,并将其放在Debug文件夹中。
//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; 
 } 

8.5 创建更友好的用户界面

8.5.1 使用缓冲输入

缓冲输入用起来比较方便,因为在把输入发送给程序之前,用户可以编辑输入。
但是,在使用输入的字符时,他也会带来麻烦,缓冲输入要求用户按下Enter键发送输入。
//猜数1
#include<stdio.h>
int main(void)
{
    int guess = 1;

    printf("Pick an integer from 1 to 100. i will try to guess");
    printf("it.\n Resopnd with a y if my guess is right and with an n if it is wrong.\n");
    printf("UN..is your number %d?\n",guess);
    while(getchar()!='y')
        printf("Well, is it %d?\n",++guess);
    printf("yes");

    return 0;
 } 

file

每次输入n时,程序打印了两条消息,这是由于程序读取n作为用户否定了数字1,然后还读取了一个换行符号作为用户否定数字2.
解决一:使用while循环丢弃输入行最后剩余的内容。包括换行符号。
while(getchar()!='y')
{
    printf("Well , it is %d",++guess);
    while(getchar()!='\n')
        contunue;       //跳过剩余的行输入
}

file

这的确解决了换行的问题。但是,该程序还是会把fp视为n。
我们利用if语句筛选其他响应。
...
char response;
...
while((response = getchar())!='y')
{
    if(response = 'n')
        printf("well,it is %d?\n",++guess);
    else
        printf("Sorry i understand only y or n\n");
    while(getchar()!='\n')
        continue;

}

file

8.5.2 混合数值和字符输入

假设程序要求用getchar()处理字符输入,用scanf()处理数值输入。
这两个函数都能很好完成任务,但是不能混用他们。
因为getchar()读取每个字符,包括空格、制表符和换行符;
而scanf()在读取数字时则会跳过空格、制表符和换行符。
Q:该程序读入一个字符和两个数字,然后根据输入的两个数字制定和的行数和列数打印该字符。

#include<stdio.h>
void display(char cr, int lines, int width);
int main(void)
{
    int ch;         //待打印字符 
    int rows,cols;  //行数和列数
    printf("Enter a character and two integers:\n");
    while((ch=getchar())!='\n')
    {
        scanf("%d %d",&rows,&cols);
        display(ch,rows,cols);
        printf("Enter a character and two integers:\n");
        printf("Enter a newline to quit.\n");
     } 
     printf("bye!\n");

     return 0;
}
void display(char cr, int lines, int width)
{
    int row,col;

    for(row=1;row<=lines;row++)
    {
        for(col =1;col<=width;col++)
                putchar(cr);
        putchar('\n');
    }

}

file

注意:该程序以int类型读取字符(这样可以作为检测EOF)
却以char类型把字符传给display()函数
若报错
强制类型转换消除。
display(char(ch),rows,cols)
跟在3后面的换行符。scanf()函数把这个换行符留在了输入队列中。和scanf()不同,getchar()不会跳过换行符,所以在进入下一轮迭代时,还没输入字符,就被gechar()读取了。而ch是程序终止条件。
解决这个问题,程序要跳过一轮输入结束与下一轮输入开始之间的所有换行符或空格。
修改之后的版本
#include<stdio.h>
void display(char cr, int lines, int width);
int main(void)
{
    int ch;         //待打印字符 
    int rows,cols;  //行数和列数
    printf("Enter a character and two integers:\n");
    while((ch=getchar())!='\n')
    {
        if((scanf("%d %d",&rows,&cols)) !=2)
            break;
        display(ch,rows,cols);
        while(getchar()!='\n')
            continue;
        printf("Enter a character and two integers:\n");
        printf("Enter a newline to quit.\n");
     } 
     printf("bye!\n");

     return 0;
}
void display(char cr, int lines, int width)
{
    int row,col;

    for(row=1;row<=lines;row++)
    {
        for(col =1;col<=width;col++)
                putchar(cr);
        putchar('\n');
    }

}

file

8.6 输入验证

在实际应用中,用户不一定会按照程序的指令行事。
用户的输入和程序期望的输入和程序期望的输入不匹配时常发生,这会导致程序运行失败。
Q:假设你编写了一个处理非负整数的循环,但是用户很可能输入一个负数。你可以使用关系表达式来排除这种情况:
long n;
scanf("%ld",&n);
while(n>=0)      //检测不再范围内的值
{

    //处理n
    scanf("%d"&n);
}
另一类潜在的陷阱是,用户可能输入错误类型的返回值,如字符q。排除这一中情况的一种方法是,检查scanf()的返回值。
scanf("%ld",&n) ==1
//改进后‘当输入的是一个整数且该整数为正时’
long n;
while (scanf("%ld",&n)==1 && n >=0)
{

    //处理n
}
让程序再用户输入错误时候,提醒错误并且返回队列。
输入实际上是字符流,可以使用getchar()函数逐字符的读取输入,甚至可以把这些想法都结合再一个函数中
long get_long(void)
{
    long input;
    char ch;
    while(scanf("%ld",input)!=1)
    {
        while((ch=getchar())!='\n')
            putchar(ch);    //处理错误的输入
        printf(" is not an integer.\n Please enter an");
        printf("integer value,such as 25,-178,or3:");
    }
    return input;
}
//使用
#include<stdio.h>
long get_long(void);
int main(void)
{
    printf("%ld",get_long());

    return 0;
 } 
long get_long(void)
{
    long input;
    char ch;
    while(scanf("%ld",&input)!=1)
    {
        while((ch=getchar())!='\n')
            putchar(ch);    //处理错误的输入
        printf(" is not an integer.\n Please enter an");
        printf("integer value,such as 25,-178,or3:");
    }

    return input;
}
该函数要把一个int类型的值读入变量input中。如果读取失败,函数则进入w'hile循环体。然后内层循环逐字符读取错误的输入。
注意,改函数丢弃该输入行的所有剩余内容。还有一个办法是,只丢弃下一个字符或单词,然后该函数提示用户再次输入。外层虚幻重复运行,直到用户成功输入整数,此时scanf()返回1.
在用户输入整数后,程序可以检查该值是否有效,
例如:要求用户输入一个上限和一个下限来定义值的范围,在该例中,你可能 希望程序检查第一个值是否大于第二个值,除此之外还要检查这些值是否在允许范围内。
例如,当前的档案查找一般不会接受1958年以前到2014年以后的查询仍无。这个任务可以在一个函数中实现。
//stdbool.h头文件
//bad_limits()
bool bad_limits(long begin,long end,long low, long high)
{
    bool not_good = false;
    if(begain > end)
    {
        printf("%d isn't smaller than %d\n",begin,end);
        not_good = true;
    }
    if(begin < low || end < low)
    {
        printf("Values must be %ld or greater.\n",low);
        not_good = true;
    }
    if(begin > high || end >high)
    {
        printf("Values must be %ld or less.\n",high);
        not_good = true;
    }

    return not_good;
}
使用上面两个函数为一个进行算术运算的函数提供整数,该函数计算特定范围内所有整数的平方和。程序限制了范围的上限是10000000,下限是-10000000
//checking.c        --输入验证
#include<stdio.h>
#include<stdbool.h>
//验证输入是一个整数
long get_long(void);
//验证范围的上下限是否有效
bool bad_limits(long begin,long end,long low,long high);
//计算a~b之间的整数平方和
double sum_squares(long a,long b);
int main(void)
{
    const long MIN = -10000000L;
    const long MAX = +10000000L;
    long start;                 //用户指定的范围最小值 
    long stop;                  //用户指定的范围最大值
    double answer;

    printf("This program computers the sum of the squares of "
    "integers in a range.\nThe lower bond should not be less "
    "than -10000000 and\n the upper bound should not more than +"
    "10000000.\nEnter the limits(enter 0 for both limits to quit:\n"
    "lower limit:");
     start = get_long(); 
     printf("upper limit:");
     stop = get_long();
     while(start!=0 || stop!=0)
     {
        if(bad_limits(start,stop,MIN,MAX))
        {
            printf("Please try again!\n");
         }
         else
         {
            answer = sum_squares(start,stop);
            printf("The sum of the squares of integers from %ld to %ld"
             "is %g\n",start,stop,answer);
         }
         printf("Enter the limits(enter 0 for both limits to quit:\n"
        "lower limit:");
        start = get_long(); 
        printf("upper limit:");
        stop = get_long();
      } 
      printf("Done.\n");

      return 0;
 } 
 long get_long(void)
{
    long input;
    char ch;
    while(scanf("%ld",&input)!=1)
    {
        while((ch=getchar())!='\n')
            putchar(ch);    //处理错误的输入
        printf(" is not an integer.\n Please enter an");
        printf("integer value,such as 25,-178,or 3:\n");
    }
    return input;
}

bool bad_limits(long begin,long end,long low, long high)
{
    bool not_good = false;
    if(begin > end)
    {
        printf("%d isn't smaller than %d\n",begin,end);
        not_good = true;
    }
    if(begin < low || end < low)
    {
        printf("Values must be %ld or greater.\n",low);
        not_good = true;
    }
    if(begin > high || end >high)
    {
        printf("Values must be %ld or less.\n",high);
        not_good = true;
    }

    return not_good;
}
double sum_squares(long a,long b)
{
    double total = 0;
    long i;
    for(i=a;i<=b;i++)
    {
        total = (double)i*(double)i+total;
    }

    return total;
}

file

8.6.1 分析程序

程序应该遵循模块化管理流,为其他函数委派任务。
它使用get_long 获取值,while循环处理值、badlimits()检查值是否有效、sum_squres()函数处理实际的计算:

8.6.2 输入流和数字

在编写处理错误输入的代码时,应该很清楚C是如何处理输入的。
is 28 12.4
在我们眼里,这就像一个由字符、整数和浮点数组成的字符串。但是对于C程序而言,这是一个字节流。
第一个字节是字母i的字符编码,第2个字节是字母s的字符编码,第3个字节是空格字符的字符编码,第4个字节是数字2的字符编码,等等。
如果get_long()函数处理这一行输入,第一个字符是非数字,那么整行输入都会被丢弃。包括其中的数字,因为这些数字只是该输入行中的其他字符:
while((ch=getchar())!='\n')
    putchar(ch);
    虽然输入流是由字符组成,但是也可以设置scanf()函数把他们转换成数值。
    42
如果在scanf()函数中使用%c转换说明,他只会读取字符4并将它存储在char类型的变量中。
如果使用%s转换说明,他会读取字符4和字符2这两个字符,并将其存储在字符数组中。如果使用%d转换说明,scanf()同样会读取两个字符,但随后会见算出他们对应的整数的值:4*10+2,然后将表示该整数的二进制数存储在int类型的变量中。
如果%f转换说明,scanf()也会读取两个字符计算出他们对应的值42.0用内部的浮点表示法表示该值,并将结果存储在float类型的变量中。
简而言之,输入由字符组成,但是scanf()可以把输入转换成整数类型或浮点类型数值。使用转换说明(如%d和%f)限制了可接受输入的字符类型,而getchar()和使用%c和scanf()接受所有的字符。

8.7 菜单浏览

两个目标:当用户遵守指令运行程序时,程序正常运行;
第二个目标,当用户没有遵循指令运行程序时,程序也能顺利运行。

8.7.1 任务

一个菜单程序,它要获取用户的响应,根据响应执行动作。程序应该提供返回菜单的选项。C的switch语句是根据选项决定行为的好工具,用户的每个选项都可以对应一个特定的case标签。
使用while语句可以实现重复访问菜单的功能。
伪代码:
获取选项
当选项不是'q'时
    转到响应的选项并执行
    获取下一个选项

8.7.2 使执行更顺利

#include<stdio.h>
char get_choice(void);
void count(void);
int main(void)
{
    int choice;

    while((choice = getchar())!='q')
    {
        switch(choice)
        {
            case'a':printf("Buy now, sell high");
            break;
            case'b':putchar('\a');
            break;
            case'c':count();
            break;
            default:printf("Program error!\n");
            break;
        }
    }

    return 0;
}
定义get_choice()函数只能返回'a','b','c'和'q'.get_choice()的用法和getchar()相同,两个函数都是获取一个值,并与终止值(该例中是'q')做比较。
default语句可以方便调试。如果get_choice()函数没能把返回值限制为菜单指定几个选项值,default语句有助于发现问题所在。
get_choice()函数
//get_choice()
显示选项
获取用户响应值
当响应不合适时
    提示用户再次输入
    获取用户的响应
char get_choice(void)
{
    int ch;
    printf("Enter the letter of your choices:\n");
    printf("a.advice           b.bell\n");
    printf("c.count                d.quit\n");
    ch = getchar();
    while((ch<'a'||ch>'d')&&ch!='q')
    {
    printf("Please respond with a,b,c,d,or q.\n");
    getchar();
    }

    return ch;
}
缓冲输入依旧带来些麻烦,程序把用户每次按下Return键产生的换行符视为错误响应。为了让界面流畅,含函数应该跳过这些换行符。
    解决方法:读取一行的第一个字符并丢弃剩余的字符。把类似于act这样的输入视为简单的a,而不是把act中的c作为选项c的一个有效的响应:
char get_choice(void)
{
    int ch;
    printf("Enter the letter of your choices:\n");
    printf("a.advice           b.bell\n");
    printf("c.count                d.quit\n");
    ch = get_first();
    while((ch<'a'||ch>'d')&&ch!='q')
    {
    printf("Please respond with a,b,c,d,or q.\n");
    getchar();
    }

    return ch;
}
char get_first(void)
{
    int ch;
    ch = getchar();
    while(getchar()!='\n')
        continue;
    return 0;
}

8.7.3 混合字符的数值输入

前面分析过混合字符和数值输入会产生一些问题,创建菜单也有这样的问题。
例如count()函数
void count(void)
{
    int n,i;
    printf("Count how for ? Enter an intefer:\n");
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        printf("%d\n",i);
}
如果输入3作为响应,scanf()会读取3并把换行符留在队列中。下次调用get_choice()将导致get_first()返回这个换行符。
重写get_first(),使其返回下一个非空白字符而不仅仅是下一个字符,即可修复这个问题。
另外一种方法是,在count()函数中清理换行符。
void count(void)
{
    int n,i;
    printf("Count how for ? Enter an intefer:\n");
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        printf("%d\n",i);
    while(getchar()!='\n')
        continue;
}
#include<stdio.h>
char get_choice(void);
void count(void);
char get_first(void);
int main(void)
{
    int choice;

    while((choice = get_choice())!='q')
    {
        switch(choice)
        {
            case'a':printf("Buy now, sell high");
            break;
            case'b':putchar('\a');
            break;
            case'c':count();
            break;
            default:printf("Program error!\n");
            break;
        }
    }

    printf("Bye!\n");

    return 0;
}
char get_choice(void)
{
    int ch;
    printf("Enter the letter of your choices:\n");
    printf("a.advice              b.bell\n");
    printf("c.count                q.quit\n");
    ch = get_first();
    while((ch<'a'||ch>'d')&&ch!='q')
    {
    printf("Please respond with a,b,c,d,or q.\n");
    ch = get_first();
    }

    return ch;
}
char get_first(void)
{
    int ch;
    ch = getchar();
    while(getchar()!='\n')
        continue;

    return ch;
}
void count(void)
{
    int n,i;
    printf("Count how for ? Enter an intefer:\n");
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        printf("%d\n",i);
    while(getchar()!='\n')
        continue;
}

file

8.8 关键概念

C程序把输入作为传入的字节流。getchar()函数把每个字符表面解释成一个字符编码。scanf()函数以同样的方式看待输入,但是根据转换说明,它可以把字符输入转换数值。许多操作系统都提供重定向,允许用文件代替键盘输入,用文件代替显示器输出。

程序通常接受特殊形式的输入。可以在设计程序时考虑用户在输入时可能犯的错误。在输入验证部分处理这些错误情况,让程序更强健友好。

对于一个小型程序,输入验证可能是代码中最复杂的部分。处理这些问题有多种方案。例如,如果用户输入错误类型的信息,可以终止,也可以给用户提供有限次或无限次机会重新输入,

8.9 本章小结

许多程序使用getchar()逐字符输入,通常,系统使用行缓冲输入,即当用户按下enter键之后输入才被传送给程序。按下Enter健也传送了一个换行符,编程时要注意处理这个换行符。ANSI C把缓冲输入作为标准
通过I/O包中的一系列函数,以统一的方式处理不同系统中的不同文件形式,是C语言的特征之一
getchar()和scanf()函数也属于这一系列。当检测到文件结尾时,这两个函数都返回EOF。在不同系统中模拟文件结尾的条件稍有不同。在UNIX系统中,在一行开始出按下ctrl+d可以模拟文件结尾条件;而dos系统中则使用ctrl+z
许多操作系统(包括UNIX和DOS)都有重定向的特征,因此可以用文件代替键盘和屏幕运行输入和输出。读到EOF即停止读取读取的程序可用于键盘输入和模拟文件结尾信号,或者用于重定位文件。
混合使用getchar()和scanf()时,如果在调用getchar()之前,scnaf()在输入行留下一个换行符,会导致一些问题。

擦肩而过的概率