第八章 字符输入输出和输入验证

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

  • **getchar()putchar()**每次只处理一个字符。

  • /********************************************************************************
    * @author: Mr.Y
    * @date: 2022/5/2 17:35
    * @description: 获取从键盘输入的字符,并把这些字符发送到屏幕上。
     *              程序使用while循环,当读到#字符时停止。
     *              存在的问题:如果用一个特殊字符(如:#)来结束输入,就无法在文本中使用这个字符,如何解决。
    ********************************************************************************/
    #include <stdio.h>
    
    int main(void)
    {
        char ch;
        
        while ((ch = getchar()) != '#')
            putchar(ch);
    
    
        return 0;
    }
    /***
     * output
     *  Hello, there. I would
     *  Hello, there. I would
     *  like a #3 bag of potatoes.
     *  like a
     */
    

8.2 缓冲区

  • 在老式系统运行上面程序可能显示如下:

    • HHeelllloo,, tthheerree.. II wwoouulldd[enter]
      lliikkee aa #
      
    • 像这样回显用户输入的字符后立即重复打印该字符是属于无缓冲输入,即正在等待的程序可立即使用输入的字符。

  • 大部分系统在用户按下Enter键之前不会重复打印刚输入的字符,这种输入形式属于缓冲输入。用户输入的字符被收集并存储在一个被称为缓冲区buffer)的临时存储区,按下Enter键后,程序才可使用用户输入的字符。

  • 为什么需要缓冲区?

    • 将若干字符作为一个块进行传输比逐个发送字符节约时间。
    • 如果输入错误,可以直接通过键盘修正错误。
  • 缓冲分类

    • 完全缓冲I/O
      • 当缓冲区被填满时才刷新缓冲区(内容被发送至目的地),通常出现在文件输入中。缓冲区的大小取决于系统,常见的大小是512字节和4096字节。
    • 行缓冲I/O
      • 在出现换行符时刷新缓冲区。键盘输入通常是行缓冲输入,所以在按下Enter键后才刷新缓冲区。
  • 使用缓冲输入还是无缓冲输入?

    • ANSI C和后续的C标准都规定输入是缓冲的。
    • ANSI决定把缓冲输入作为标准的原因是:一些计算机不允许无缓冲输入

8.3 结束键盘输入

8.3.1 文件,流和键盘输入

  • 文件file)是存储器中存储信息的区域。通常,文件都保存在某种永久存储器中(如,硬盘,U盘等)
  • 编写的C程序就保存在文件中,用来编译C程序的程序也保存在文件中。后者说明,某些程序需要访问指定的文件,当编译存储在名为echo.c文件中的程序时,编译器打开echo.c文件并读取其中的内容。当编译器处理完后,会关闭文件。其他程序,如文字处理器,不仅要打开,读取和关闭文件,还要把数据写入文件。
  • C有很多用于打开,读取,写入和关闭文件的库函数。从较低层面上,C可以使用主机操作系统的基本文件工具直接处理文件,这些直接调用操作系统的函数被称为底层I/Olow-level I/O)。由于计算机系统各不相同,所以不可能为普通的底层I/O函数创建标准库,ANSI C也不打算这样做。然而从较高层面上,C还可以通过标准I/O包standard I/O package)来处理文件。
  • 从概念上看,C程序处理的是而不是直接处理文件stream)是一个实际输入或输出映射的理想化数据流。这意味着不同属性和不同种类的输入,由属性更统一的流来表示。于是,打开文件的过程就是把流与文件相关联,而且读写都通过流来完成。
  • stdin流表示键盘输入,stdout流表示屏幕输出。getchar()putchar()printf()scanf()函数都是标准I/O包的成员,处理这两个流。

8.3.2 文件结尾

  • 检测文件结尾的一种方法是,在文件末尾放一个特殊的字符标记文件结尾。

  • 如今,操作系统可以使用内嵌的Ctrl+Z字符来标记文件结尾。这曾经是操作系统使用的唯一标记,不过现在有一些其他的选择,如记录文件的大小。所以现代的文本文件不一定有嵌入的Ctrl+Z,但是如果有,该操作系统会将其视为一个文件结尾标记。

  • 操作系统使用的另一种方法是存储文件大小的信息。如果文件有3000字节,程序在读到3000字节时便达到文件的末尾。

  • C语言中,用getchar()读取文件检测到文件结尾时将返回一个特殊的值,即EOFscanf()函数检测到文件结尾时也返回EOF。通常,EOF定义在stdio.h文件中:

    • #define EOF (-1)
      
    • 为什么是**-1**?因为getchar()函数的返回值通常介于0—127,这些值对应标准字符集。但是,如果系统能够识别扩展字符集,该函数的返回值可能在0—255。无论哪种情况,-1都不对应任何字符,所以,该值可用于标记文件结尾。

  • 某些系统也许把EOF定义为**-1以外的值,但是定义的值一定与输入字符所产生的返回值不同。如果包含stdio.h文件,并使用EOF符号,就不必担心EOF**值不同的问题。

  • 如果正在读取的是键盘输入不是文件会怎样?绝大多数系统都有办法通过键盘模拟文件结尾条件。如下代码:

  • /********************************************************************************
     * echo_eof.c
    * @author: Mr.Y
    * @date: 2022/5/3 18:54
    * @description: 重复输入,直到文件结尾
    ********************************************************************************/
    #include <stdio.h>
    int main(void)
    {
        int ch;
    
        while ((ch = getchar()) != EOF)
    
            putchar(ch);
    
        return 0;
    }
    
    
    
  • 以上程序注意事项

    1. 不用定义EOF,因为stdio.h中已经定义过了。
    2. 不用担心EOF的实际值,因为EOFstdio.h中用**#define预处理指令定义,可直接使用,不必再编写代码假定EOF**为某值。
    3. 变量ch的类型从char变为int,因为char类型的变量只能表示0—255的无符号整数,但是EOF的值是**-1**。还好,getchar()函数实际返回值的类型是int,所以它可以读取EOF字符。如果实现使用有符号的char类型,也可以把ch声明为char类型,但最好还是用更通用的形式。
    4. 由于getchar()函数的返回类型是int,如果把getchar()的返回值赋给char类型的变量,一些编译器会警告可能丢失数据。
    5. ch是整数不会影响putchar(),该函数任然会打印等价的字符。
    6. 使用该程序进行键盘输入,要设法输入EOF字符,不能只输入字符EOF,也不能只输入**-1**。正确的方法是,必须找出当前系统的要求。如,在大多数UNIXLinux系统中,在一行开始处按下Ctrl+D会传输文件结尾信号。许多微型计算机系统都把一行开始处的Ctrl+Z识别为文件结尾信号,一些系统把任意位置的Ctrl+Z解释为文件结尾信号。

8.4 重定向和文件

  • 输入和输出涉及函数,数据和设备。输入设备(假设)是键盘,输入数据流由字符组成。假设你希望输入函数和数据类型不变,仅改变程序查找数据的位置。那么,程序如何知道去哪里查找输入?
  • 在默认情况下,C程序使用标准I/O包查找标准输入作为输入源。即前面介绍的stdin流,它是把数据读入计算机的常用方式。现代计算机非常灵活,可以让它到别处查找输入。尤其是,可以让一个程序从文件中查找输入,而不是键盘。
  • 程序可以通过两种方式使用文件。
    1. 显式使用特定的函数打开文件,关闭文件,读取文件,写入文件,诸如此类。
    2. 设计能与键盘和屏幕互动的程序,通过不同的渠道重定向输入至文件和从文件输出。
  • 重定向的一个主要问题是它与操作系统有关,与C无关。

8.4.1 UNIX,Linux和DOS重定向

  • UNIX(运行命令行模式时),Linuxditto)和Window命令行提示(模仿旧式DOS命令行环境)都能重定向输入,输出。重定向输入让程序使用文件而不是键盘输入,重定向输出让程序输出至文件而不是屏幕。

1,重定向输入

  • 将文件内容使用程序输入至控制台。

  • 假设已经编译了上面程序中的echo_eof.c程序,并生成一个名为echo_eof(在windows系统中名为echo_eof.exe)的可执行文件。运行程序,输入可执行文件名称。

在这里插入图片描述

  • 程序运行与前面描述一样,获取用户从键盘输入的输入,假设现在要处理名为xxx的文本文件。只需使用下面的命令代替上面的命令。

    • 可执行文件名 < 文本文件名
      
    • <符号是UNIXDOS/Windows的重定向运算符。该运算符使words文件与stdio流相关联,把文件中的内容导入echo_eof程序。

在这里插入图片描述

2,重定向输出

  • 将控制台内容输出至文件。

  • 假设要用echo_eof.exe程序将键盘输入的内容发送到名为myworlds的文件中。使用以下命令并开始键盘输入。

    • 可执行文件名 > 文本文件名
      
    • 符号**>是第二个重定向运算符。它创建一个名为xxx的文件,然后把可执行文件的输出重定向至该文件中。重定向把stdout从显示设备赋给xxx文件。如果已经有一个名为xxx**的文件,通常会擦除该文件的内容,然后替换新的内容。

在这里插入图片描述

3,组合重定向

  • 假设你需要制作一份myworlds文件的副本,并命名为savewords。只需输入以下命令即可:

在这里插入图片描述

  • 使用下面的命令也可以,因为命令与重定向运算符的顺序无关。

    • echo_eof.exe > savewords < myworlds
      
  • 在一条命令中,输入文件名和输出文件名不能相同。

  • 在系统中使用两个重定向运算符(<和>)时,需要遵守以下原则。

    1. 重定向运算符连接一个可执行程序和一个数据文件,不能用于连接一个数据文件和另一个数据文件,也不能用于连接一个程序和另一个程序。
    2. 使用重定向运算符不能读取多个文件的输入,也不能把输出定向至多个文件。
    3. 通常,文件名和运算符之间的空格不是必须的,除非是偶尔在UNIX shellLinux shellWindows命令行提示模式中使用的有特殊含义的字符。
  • UNIXLinuxWindows/DOS还有**>>运算符,该运算符可以把数据添加到现有文件的末尾,而|**运算符能把一个文件的输出连接到另一个文件的输入。

8.5 友好的用户界面

8.5.1 使用缓冲输入

  • 缓冲输入用起来比较方便,因为在把输入发送给程序之前,用户可以编辑输入。但是在使用输入的字符时,也会带来麻烦,缓冲输入要求用户按下Enter键发送输入。这一动作也传送了换行符,程序必须妥善处理这个麻烦的换行符。

  • /********************************************************************************
    * @author: Mr.Y
    * @date: 2022/7/19 15:21
    * @description: 用户选择一个数字,程序猜用户选择的数字是几。
     * 用户只能输入'n'或'y'
    ********************************************************************************/
    
    #include <stdio.h>
    int main(void)
    {
        int guess = 1;
        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 and with");
        printf("\nan n if it is wrong.\n");
        printf("Uh...is your number %d?\n",guess);
        while ((response = getchar()) != 'y')         /*获取响应*/
        {
            if (response == 'n')
                printf("Well, then, is it %d\n",++guess);
            else
                printf("Sorry, I understand only y or n.\n");
            while (getchar() != '\n')
                continue;           /*跳过剩余的输入行*/
        }
    
        return 0;
    }
    /**
    *output
    * D:\code\CLion\C_Primer_Plus\cmake-build-debug\guess.exe
    * Pick an integer from 1 to 100. I will try to guess it.
    * Respond with a y if my guess is right and with
    * an n if it is wrong.
    * Uh...is your number 1?
    * 1
    * Sorry, I understand only y or n.
    * n
    * Well, then, is it 2
    * q
    * Sorry, I understand only y or n.
    * y
    
    Process finished with exit code 0
    **/
    
    

8.5.2 混合数值和字符输入

  • 假设程序要求用**getchar()处理字符输入,用scanf()处理数值输入,这两个函数都能很好地完成任务,但是不能把它们混用。因为getchar()读取每个字符,包括空格,制表符和换行符;而scanf()**在读取数字时则会跳过空格,制表符和换行符。

  • /********************************************************************************
    * @author: Mr.Y
    * @date: 2022/7/20 18:23
    * @description: 程序读入一个字符和两个数字,然后根据输入的两个数字指定的行数和列数打印该字符
    ********************************************************************************/
    #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 another 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');
    
        }
    }
    /*
     * output
     * Enter a character and  two integers:
     * w 3 4
     * wwww
     * wwww
     * wwww
     * Enter another character and two integers;
     * Enter a newline to quit.
     * t 3 6
     * tttttt
     * tttttt
     * tttttt
     * Enter another character and two integers;
     * Enter a newline to quit.
    
     * Bye.
     * */
    
    
    
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值