C

C 知识量:16 - 74 - 317

8.1 字符的输入/输出><

单字符I/O- 8.1.1 -

getchar()和putchar()每次只处理一个字符,是单字符输入输出函数(其实,getchar()和putchar()不是真正的函数,它们被定义为供预处理器使用的宏)。C将stdio.h头文件与使用getchar()和putchar()相关联,因此,使用它们的程序需要首先包含这个头文件。示例:

#include <stdio.h>

int main(void) {
    char ch;
    while ((ch = getchar()) != '#') {
        putchar(ch);
    }
    system("pause");
    return 0;
}

以上程序在while循环中使用getchar()获取用户输入的字符,然后打印输入的字符,如果输入的是“#”,则结束循环。

缓冲区- 8.1.2 -

对于上一个示例,在大部分系统中,用户输入字符,在按下Enter键之前不会重复打印刚输入的字符,这种输入形式属于缓冲输入。而当用户输入字符后,不等按下Enter键就重复打印刚输入的字符的,则属于无缓冲输入。

对于缓冲输入,输入的字符被收集并储存在一个被称为缓冲区(buffer)的临时存储区,按下Enter键后,程序才可以使用用户输入的字符。

缓冲区的好处有很多,例如:

  • 把若干字符作为一个块进行传输比逐个发送这些字符要节约时间。

  • 如果用户打错字符,可以直接通过键盘修正错误,从而确保按下Enter键时,传输的是正确的输入。

虽然缓冲区输入好处很多,但是某些交互式程序也需要无缓冲输入,按下一个键就希望立刻执行相应的指令,因此,缓冲输入和无缓冲输入都有用武之地。C语言目前的输入是缓冲输入,因为一些计算机不允许无缓冲输入。

缓冲分为两类:

  • 完全缓冲I/O:是指当缓冲区被填满时才刷新缓冲区(即内容被发送至目的地),通常用于文件输入中。

  • 行缓冲I/O:是指在出现换行符时刷新缓冲区。键盘输入通常是行缓冲输入,按下Enter后才刷新缓冲区。

文件、流和键盘输入- 8.1.3 -

文件(file)是存储器中储存信息的区域。通常,文件都保存在某种永久存储器中(例如:硬盘、U盘或DVD等)。

C有许多用于打开、读取、写入和关闭文件的库函数。从较低层面上,C可以使用主机操作系统的基本文件工具直接处理文件,这些直接调用操作系统的函数被称为底层I/O。从较高层面上,C还可以通过标准I/O包来处理文件。这涉及创建用于处理文件的标准模型和一套标准I/O函数。在这一层面上,具体的C实现负责处理不同操作系统的差异,以便为用户提供统一的使用界面。

操作系统的差异包括文件存储以及标记行末尾的方式不同等。使用C标准的I/O包,就不用考虑这些差异了。

从概念上看,C程序处理的是流而不是直接处理文件。流(stream)是一个实际输入或输出映射的理想化数据流。不同属性和不同种类的输入,由属性更统一的流来表示。打开文件的过程就是把流与文件相关联,读写都通过流来完成。

C把输入和输出设备视为存储设备上的普通文件,把键盘和显示设备视为每个C程序自动打开的文件。stdin流表示键盘输入,stdout流表示屏幕输出。getchar()、putchar()、printf()和scanf()函数都是标准I/O包的成员,以便处理这两个流。

文件结尾- 8.1.4 -

计算机操作系统要以某种方式判断文件的开始和结束。检测文件结尾的一种方法是,在文件末尾放一个特殊的字符标记文件结尾。例如使用Ctrl+Z。另一种检测文件结尾的方法是存储文件的大小信息。例如标记某个文件有2000个字节,程序读取到2000字节的时候就知道到达了文件末尾,这种方法可以在文件中存储所有的字符,包括Ctrl+Z。

无论操作系统如何检测文件结尾,在C语言中,用getchar()读取文件时,当检测到文件结尾时将返回一个特殊值:EOF,即end of file的缩写。scanf()函数也是如此。

EOF定义在stdio.h头文件中:#define EOF (-1)。之所以定义为-1,是因为getchar()函数返回值通常介于0~127,这些数值对应标准字符集。如果系统能识别扩展字符集,返回值可能介于0~255之间。但无论怎样,-1都不对应任何字符,因此,使用该值标记文件结尾。需要理解EOF是一个标志着检测到文件结尾的值,并不是一个能够在文件中找得到的符号。

示例:

#include <stdio.h>

int main(void) {
    int ch;
    while ((ch = getchar()) != EOF) {
        putchar(ch);
    }
    system("pause");
    return 0;
}

运行的结果为:(使用Ctrl+Z模拟文件结尾)

3
3
5
5
f
f
^Z

需要注意以下几点:

  • 不用定义EOF,因为stdio.h中已经定义过了。

  • 不用担心EOF的实际值,因为EOF在stdio.h中用#define预处理指令定义,可直接使用,不必再编写代码假定EOF为某值。

  • 变量ch的类型为int,因为char类型的变量只能表示0~255的无符号整数,但是EOF的值是-1。getchar()函数的实际返回值类型是int,所以它可以读取EOF字符。

  • 由于getchar()函数的返回类型是int,如果把getchar()的返回值赋给char类型的变量,一些编译器会警告可能丢失数据。

  • ch是整数不会影响putchar()函数,它仍然会打印等价的字符。

  • 使用以上示例程序时,要设法输入EOF字符,模拟文件结尾。不能在命令行中直接输入字符EOF,也不能输入-1,正确的方法是输入Ctrl+Z,命令行会显示^Z,并结束循环。

重定向和文件- 8.1.5 -

C程序可以通过两种方式使用文件。一种是显式使用特定函数打开文件、关闭文件、读取文件、写入文件等等。另一种是设计能与键盘和屏幕互动的程序,通过不同渠道重定向输入至文件和从文件输出,即把stdin流重新赋给文件。

1、重定向输入。

假设编写了一个名为echo.c的程序,编译后成为可执行文件echo.exe。该程序可以通过键盘获取用户的输入。现在如果以文本文件words(文本文件指内含文本的文件,其数据是我们可直接识别的字符)代替键盘输入,可以使用下面的命令:

echo < words

<符号是UNIX和DOS/Windows的重定向运算符。该运算符使文本文件与stdin流相关联,把文件的内容导入echo程序中。echo程序本身并不知道也不关心输入的内容究竟是来自键盘还是文件,它只知道这是要输入的字符流。

2、重定向输出。

如果要用echo程序把键盘输入的内容发送(保存)到名为mywords的文件中,可以使用下面的命令:

echo > mywords

>符号是第2个重定向运算符。它创建一个名为mywords的新文件,然后把echo的输出重定向至该文件。重定向把stdout流从显示设备(显示器)赋给mywords文件。如果事前已经有一个名为mywords的文件,通常会擦出该文件的内容,然后替换新的内容。

3、组合重定向。

如果希望将mywords文件的内容复制到一个新的名为newwords的文件中,即从文件到文件的复制,可以使用组合重定向,具体命令为:

echo < mywords > newwords

也可以使用:

echo > newwords < mywords

可见,命令与重定向运算符的顺序无关。

在UNIX、Linux或Windows/DOS系统中使用两个重定向运算符(<和>)时,需要遵循以下原则:

  • 重定向运算符连接一个可执行程序和一个数据文件,但不能用于连接一个数据文件和另一个数据文件,也不能用于连接一个程序和另一个程序。

  • 使用重定向运算符不能同时读取多个文件的输入,也不能同时把输出定向至多个文件。

  • 通常,文件名和运算符之间的空格不是必须的,除非是偶尔在UNIX shell、Linux shell或Windows命令行提示模式中使用的有特殊含义的字符。