美文网首页C Primer Plus(6th)
文件、 流和键盘输入、重定向和文件

文件、 流和键盘输入、重定向和文件

作者: akuan | 来源:发表于2020-11-09 18:24 被阅读0次

    文件(file) 是存储器中储存信息的区域。 通常, 文件都保存在某种永久存储器中(如, 硬盘、 U盘或DVD等) 。 毫无疑问, 文件对于计算机系统相当重要。 例如, 你编写的C程序就保存在文件中, 用来编译C程序的程序也保存在文件中。 后者说明, 某些程序需要访问指定的文件。 当编译储存在名为 echo.c 文件中的程序时, 编译器打开echo.c文件并读取其中的内容。 当编译器处理完后, 会关闭该文件。 其他程序, 例如文字处理器, 不仅要打开、 读取和关闭文件, 还要把数据写入文件。
    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 的输入函数内置了文件结尾检测器。 既然可以把键盘输入视为文件, 那么也应该能使用文件结尾检测器结束键盘输入。
    计算机操作系统要以某种方式判断文件的开始和结束。 检测文件结尾的一种方法是, 在文件末尾放一个特殊的字符标记文件结尾。 CP/M、 IBMDOS和MS-DOS的文本文件曾经用过这种方法。 如今, 这些操作系统可以使用内嵌的Ctrl+Z字符来标记文件结尾。 这曾经是操作系统使用的唯一标记,不过现在有一些其他的选择, 例如记录文件的大小。 所以现代的文本文件不一定有嵌入的Ctrl+Z, 但是如果有, 该操作系统会将其视为一个文件结尾标记。 下图演示了这种方法。

    image.png
    操作系统使用的另一种方法是储存文件大小的信息。 如果文件有3000字节, 程序在读到3000字节时便达到文件的末尾。 MS-DOS 及其相关系统使用这种方法处理二进制文件, 因为用这种方法可以在文件中储存所有的字符,包括Ctrl+Z。 新版的DOS也使用这种方法处理文本文件。 UNIX使用这种方法处理所有的文件。
    无论操作系统实际使用何种方法检测文件结尾, 在C语言中, 用getchar()读取文件检测到文件结尾时将返回一个特殊的值, 即EOF(end of file的缩写) 。 scanf()函数检测到文件结尾时也返回EOF。 通常, EOF定义在stdio.h文件中:
    #define EOF (-1)
    

    为什么是-1? 因为getchar()函数的返回值通常都介于0~127, 这些值对应标准字符集。 但是, 如果系统能识别扩展字符集, 该函数的返回值可能在0~255之间。 无论哪种情况, -1都不对应任何字符, 所以, 该值可用于标记文件结尾。某些系统也许把EOF定义为-1以外的值, 但是定义的值一定与输入字符所产生的返回值不同。 如果包含stdio.h文件, 并使用EOF符号, 就不必担心EOF值不同的问题。 这里关键要理解EOF是一个值, 标志着检测到文件结尾, 并不是在文件中找得到的符号。
    那么, 如何在程序中使用EOF? 把getchar()的返回值和EOF作比较。 如果两值不同, 就说明没有到达文件结尾。 也就是说, 可以使用下面这样的表达式:

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

    如果正在读取的是键盘输入不是文件会怎样? 绝大部分系统(不是全部) 都有办法通过键盘模拟文件结尾条件。 例如, 在大多数UNIX和Linux系统中, 在一行开始处按下Ctrl+D会传输文件结尾信号。 许多微型计算机系统都把一行开始处的Ctrl+Z识别为文件结尾信号, 一些系统把任意位置的Ctrl+Z解释成文件结尾信号。

    重定向文件

    输入和输出涉及函数、 数据和设备。 例如, 考虑 echo_eof.c, 该程序使用输入函数 getchar()。 输出设备(我们假设) 是键盘, 输入数据流由字符组成。 假设你希望输入函数和数据类型不变, 仅改变程序查找数据的位置。 那么, 程序如何知道去哪里查找输入?
    在默认情况下, C程序使用标准I/O包查找标准输入作为输入源。 这就是前面介绍过的stdin流, 它是把数据读入计算机的常用方式。 它可以是一个过时的设备, 如磁带、 穿孔卡或电传打印机, 或者(假设) 是键盘, 甚至是一些先进技术, 如语音输入。 然而, 现代计算机非常灵活, 可以让它到别处查找输入。 尤其是, 可以让一个程序从文件中查找输入, 而不是从键盘。
    程序可以通过两种方式使用文件。 第 1 种方法是, 显式使用特定的函数打开文件、 关闭文件、 读取文件、 写入文件, 诸如此类。 我们在第13章中再详细介绍这种方法。 第2种方法是, 设计能与键盘和屏幕互动的程序, 通过不同的渠道重定向输入至文件和从文件输出。 换言之, 把stdin流重新赋给文件。 继续使用getchar()函数从输入流中获取数据, 但它并不关心从流的什么位置获取数据。 虽然这种重定向的方法在某些方面有些限制, 但是用起来比较简单, 而且能让读者熟悉普通的文件处理技术。
    重定向的一个主要问题与操作系统有关, 与C无关。 尽管如此, 许多C环境中(包括UNIX、 Linux和Windows命令提示模式) 都有重定向特性, 而且一些C实现还在某些缺乏重定向特性的系统中模拟它。 在UNIX上运行苹果OS X, 可以用UNIX命令行模式启动Terminal应用程序。 接下来我们介绍UNIX、 Linux和Windows的重定向。

    UNIX、 Linux和DOS重定向:

    重定向输入让程序使用文件而不是键盘来输入, 重定向输出让程序输出至文件而不是屏幕。
    1.重定向输入
    假设已经编译了echo_eof.c 程序, 并把可执行版本放入一个名为echo_eof(或者在Windows系统中名为echo_eof.exe) 的文件中。 运行该程序, 输入可执行文件名:echo_eof
    该程序的运行情况和前面描述的一样, 获取用户从键盘输入的输入。 现在, 假设你要用该程序处理名为words的文本文件。 文本文件(text file) 是内含文本的文件, 其中储存的数据是我们可识别的字符。 文件的内容可以是一篇散文或者C程序。 内含机器语言指令的文件(如储存可执行程序的文件) 不是文本文件。 由于该程序的操作对象是字符, 所以要使用文本文件。只需用下面的命令代替上面的命令即可:
    echo_eof < words
    “<”符号是UNIX和DOS/Windows的重定向运算符。 该运算符使words文件与stdin流相关联, 把文件中的内容导入echo_eof程序。 echo_eof程序本身并不知道(或不关心) 输入的内容是来自文件还是键盘, 它只知道这是需要导入的字符流, 所以它读取这些内容并把字符逐个打印在屏幕上, 直至读到文件结尾。 因为C把文件和I/O设备放在一个层面, 所以文件就是现在的I/O设备。
    2.重定向输出
    现在假设要用echo_eof把键盘输入的内容发送到名为mywords的文件中。 然后, 输入以下命令并开始输入:
    echo_eof>mywords
    “>”符号是第2个重定向运算符。 它创建了一个名为mywords的新文件, 然后把echo_eof的输出(即, 你输入字符的副本) 重定向至该文件中。 重定向把stdout从显示设备(即, 显示器) 赋给mywords文件。 如果已经有一个名为mywords的文件, 通常会擦除该文件的内容, 然后替换新的内容(但是, 许多操作系统有保护现有文件的选项, 使其成为只读文件) 。 所有出现在屏幕的字母都是你刚才输入的, 其副本储存在文件中。 在下一行的开始处按下Ctrl+D(UNIX) 或Ctrl+Z(DOS) 即可结束该程序。
    正面看一个用函数方法完成打开文件并显示内容的例子:

    #include <stdio.h>
    #include <stdlib.h> // for exit()
    int main() {
        int ch;
        FILE *fp;
        char fname[50]; // to hold the file name
        printf("Enter the name of the file: ");
        scanf("%s", fname);
        fp = fopen(fname, "r"); // open file for reading
        if (fp == NULL) { // attempt failed
            printf("Failed to open file. Bye\n");
            exit(1); // quit program
        }
        // getc(fp) gets a character from the open file
        while ((ch = getc(fp)) != EOF)
            putchar(ch);
        fclose(fp); // close the file
    
        return 0;
    }
    

    相关文章

      网友评论

        本文标题:文件、 流和键盘输入、重定向和文件

        本文链接:https://www.haomeiwen.com/subject/wiwtbktx.html