字符输入输出
输入输出、有无缓冲的区别
键盘模拟文件结尾(EOF)条件
重定向链接程序和文件
更友好的用户界面
用于输入输出的函数简称 I/O 函数,如 printf()
scanf()
getchar()
putchar()
。这些 IO 函数负责把信息传送到程序中。
最初输入输出函数并不是 C 定义的一部分,C 标准把这些函数交给编译器的实现者来实现。UNIX 系统中有这些函数的实行,ANSI C 库借鉴了成功经验,把大量的 UNIX I/O 函数囊括其中。
C 作为底层语言需要保证这些标准函数在不同系统上都能使用,所以避免使用特殊系统才有的特性。因此有些供应商会利用硬件特性实现更多的 I/O 函数。这里只讨论所有计算机系统都通用的标准 I/O 函数。使用标准 IO 函数写的程序很容易从一个系统移植到另一个系统。
单字符 IO getchar()
和 putchar()
getchar()
和 putchar()
这两个函数每次只处理一个字符,这种方法很适合计算机,这也是绝大多数文本处理程序使用的核心方法。
自 ANSI C 标准后,这两个函数就关联在 stdio.h
文件中。事实上,这两个不是真正的函数,这两个是宏。
这段代码会读取键盘输入,直到遇到 #
退出。首先需要了解 C 程序如何处理键盘输入,尤其是缓冲和标准输入文件的概念。
输入之后并没有回显,这些内容是存放在缓冲区的,
缓冲区
如果没有缓冲区,上面这段代码在每收到一个字符就会立刻显示出来,正在等待的程序可以立即获得输入的字符。大部分系统需要用户按下 ENTER 之前不会重复打印。用户输入的字符都被收集并存储在缓冲区(buffer),按下 ENTER 后,缓冲区的内容送给程序。
缓冲区的存在也是出于系统运行效率考虑的。每个字符单独传输的效率要比打包传输低得多。(内核态与硬件交互,集中起来送给用户态程序,更多细节参考操作系统课程)。
并不是所有程序都需要缓冲区,如游戏程序,使用缓冲区是不明智的。
缓冲分为两类:完全缓冲I/O 和 行缓冲I/O。
完全缓冲输入:当缓冲区满时才刷新缓冲区(内容发送至目的地)
常出现在文件输入
缓冲区大小取决于系统,常见 512 字节和 4096 字节
行缓冲输入:键盘输入,按下 ENTER 才刷新缓冲区
ANSI C 和后续的 C 标准都规定输入是缓冲的。
UNIX 系统可以使用 ioctl()
函数指定输入的类型,然后用 getchar 执行操作。
结束键盘输入
上面程序中的 #
也是一个普通字符,更好的方法是用一个在文本中用不到的字符来标记输入完成,这样的字符不会再文本中出现,C 提供了这样的字符,即 EOF
,linux 里按键组合为 ctrl + D
文件是对存储数据的抽象,可以将文件视为一个字节序列,C 中有处理文件的函数,从概念上看,C 程序处理的是流而不是直接处理文件。流即字节流。这里提出文件,是要理解把键盘输入输出也可以视为字节流,即文件。可以把键盘和显示器视为 C 程序自动打开的文件,stdin
为键盘输入,stdout
为屏幕输出。(linux 文件描述符012,更多内容查看操作系统)。
在处理文件时,C 是可以检测到文件尾的,既然把键盘输入视为文件流的输入,那么也应该可以使用检测文件结尾的方法检测结束键盘输入。
文件结尾,早期 CP/M、IBM-DOS、MS-DOS 的方法是在文件尾放一个特殊字符即ctrl + Z
,现在也有可以使用文件大小判断文件结尾,但是如果有这个字符标记,那么一定就是文件结尾。使用 getchar
读取时,会返回一个 EOF
。
现在这个程序可以把用户输入的内容复制到屏幕上。假如通过某种方式可以把一个文件传送给这个程序,那么岂不是可以把这个文件显示在屏幕i上?或者把键盘输入的内容保存到一个文件中?所以这段小程序有查看文件内容、创建新文件、复制文件的潜力,问题的关键是我们需要编程控制输入输出流。
在这个代码中,需要键盘输入 CTRL + D 来模拟文件结束。
重定向和文件
除了用键盘输入,向屏幕输出,也可以从文件输入,输出到文件,这就是重定向。
比如
这些文本文件在读到结尾时,操作系统会自动返回一个 EOF ,程序也就结束了。
最后更新于