1.5 字符输入/输出
接下来我们看一组与字符型数据处理有关的程序
许多程序只不过是这里所讨论的程序原型的扩充版本而已
标准库提供的输入/输出模型非常简单
无论文本从何处输入,输出到何处,其输入/输出都是按照字符流的方式处理
文本流是由多行字符构成的字符序列,而每行字符则由 0 个或多个字符组成,行末是一个换行符
标准库负责使每个输入/输出流都能够遵守这一模型
使用标准库的 C 语言程序员不必关心在程序之外这些行是如何表示的
标准库提供了一次读/写一个字符的函数,其中最简单的是 getchar
和 putchar
两个函数
每次调用时,getchar
函数从文本流中读入下一个输入字符,并将其作为结果值返回
也就是说,在执行语句 c = getchar()
之后,变量 c 中将包含输入流中的下一个字符
这种字符通常是通过键盘输入的
每次调用 putchar
函数时将打印一个字符
例如,语句 putchar()
将把整型变量 c
的内容以字符的形式打印出来,通常是显示在屏幕上
putchar
与 printf
这两个函数可以交替调用,输出的次序与调用的次序一致
1.5.1 文件复制
借助于 getchar
与 putchar
函数,可以在不了解其它输入/输出知识的情况下编写出数量惊人的有用的代码
最简单的例子就是把输入一次一个字符地复制到输出,其基本思想如下:
读一个字符
while (该字符不是文件结束指示符)
输出刚读入的字符
读下一个字符
将上述基本思想转换为 C 语言程序为:
#include <stdio.h>
/* copy input to output; 1st version */
main()
{
int c;
c = getchar();
while (c != EOF) {
putchar(c);
c = getchar();
}
}
关系运算符 !=
表示 “ 不等于 ”
[vagrant@node01 1.5]$ gcc -o test01 test01.c
[vagrant@node01 1.5]$ ls
test01 test01.c
[vagrant@node01 1.5]$ ./test01
aa
aa
abc
abc
getchar()
在没有字符时会阻塞等待键盘输入
等待键盘输入时,控制流会传递给 shell
此时输入的字符是被 shell 接收的,也是被 shell 镜像打印出来的,让用户可以看到自己输入了什么
shell 的操作是以行为单位的(所以叫命令行)
即当输入回车键时,shell 会将这一行的输入(包括回车)以及执行权通过异常控制流传递回 C 程序
C 程序会重新执行getchar()
函数的第一条指令
这次能够获取到字符不会再阻塞,循环打印,直到这一行的字符打印完了(包括回车)将再次阻塞并将执行权传递给 shell
在 shell 中输入 Ctrl + d 时等价于输入
EOF
char
类型专门用于存储字符型数据,任何整型 int
也可以用于存储字符型数据
因为某些潜在的重要原因,我们在此使用 int
类型
这里需要解决如何区分文件中有效数据与输入结束符的问题
C 语言采取的解决方法是:在没有输入时,getchar
函数将返回一个特殊值,这个特殊值与任何实际字符都不同
这个值称为 EOF
(end of file,文件结束)
我们在声明变量 c
的时候,必须让它大到足以存放 getchar
函数返回的任何值
这里之所以不把 c
声明成 char
类型,是因为它必须足够大
除了能存储任何可能的字符外还要能存储文件结束符 EOF
,因此,我们将 c
声明成 int
类型
EOF
定义在头文件 <stdio.h>
中,是个整型数
其具体数值是什么并不重要,只要它与任何 char
类型的值都不相同即可
这里使用符号常量,可以确保程序不需要依赖于其对应的任何特定的数值
对于经验比较丰富的 C 语言程序员,可以把这个字符复制程序编写得更精炼一些
在 C 语言中,类似于 c = getchar()
之类的赋值操作是一个表达式,并且具有一个值(即:赋值后左边变量保存的值)
程序可改写为:
#include <stdio.h>
/* copy input to output; 2nd version */
main()
{
int c;
while ((c = getchar()) != EOF)
putchar(c);
}
[vagrant@node01 1.5]$ gcc -o test02 test02.c
[vagrant@node01 1.5]$ ls
test01 test01.c test02 test02.c
[vagrant@node01 1.5]$ ./test02
aa
aa
abc
abc
赋值表达式两边的圆括号不能省略
因为不等于运算符 !=
的优先级比赋值运算符 =
的优先级要高
在不使用圆括号的情况下关系测试 !=
将在赋值 =
操作之前执行
即:语句 c = getchar() != EOF
等价于语句 c = (getchar() != EOF)
该语句执行后 c
的值将被置为 0
或 1
(取决于调用 getchar
函数时是否碰到文件结束标志)
打印 EOF 看看:
#include <stdio.h>
/* copy input to output; 2nd version */
main()
{
putchar(EOF);
putchar('\n');
}
[vagrant@node01 1.5]$ gcc -o test03 test03.c
[vagrant@node01 1.5]$ ./test03
�