2022-11-12字符输入/输出和输入验证

I/O函数(如printf()、 scanf()、 getchar()、 putchar()等) 负责把信息传送到程序中。

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

C标准发布以后, C就把stdio.h头文件与使用getchar()和putchar()相关联, 这就是为什么程序中要包含这个头文件的原因(其实,getchar()和 putchar()都不是真正的函数, 它们被定义为供预处理器使用的宏

缓冲区

回显用户输入的字符后立即重复打印该字符是属于无缓冲(或直接) 输入, 即正在等待的程序可立即使用输入的字符。

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

缓冲区

把若干字符作为一个块进行传输比逐个发送这些字符节约时间。 其次, 如果用户打错字符, 可以直接通过键盘修正错误。 当最后按下Enter键时, 传输的是正确的输入。

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

ANSI C决定把缓冲输入作为标准的原因是: 一些计算机不允许无缓冲输入。UNIX系统使用另一种不同的方式控制缓冲。 在UNIX系统中, 可以使用ioctl()函数(该函数属于UNIX库, 但是不属于C标准) 指定待输入的类型, 然后用getchar()执行相应的操作。

在ANSI C中, 用setbuf()和setvbuf()函数控制缓冲, 但是受限于一些系统的内部设置, 这些函数可能不起作用。 总之, ANSI没有提供调用无缓冲输入的标准方式, 这意味着是否能进行无缓冲输入取决于计算机系统。

结束键盘输入

#也是一个普通的字符, 有时不可避免要用到。 应该用一个在文本中用不到的字符来标记输入完成, 这样的字符不会无意间出现在输入中, 在你不希望结束程序的时候终止程序。

文件、流和键盘输入

文件(file) 是存储器中储存信息的区域。 通常, 文件都保存在某种永久存储器中(如, 硬盘、 U盘或DVD等) 。文件对于计算机系统相当重要。

C可以使用主机操作系统的基本文件工具直接处理文件, 这些直接调用操作系统的函数被称为底层 I/O (low-level I/O)。 C还可以通过标准I/O包(standard I/O package) 来处理文件。

不同的系统储存文件的方式不同。有些系统把文件的内容储存在一处, 而文件相关的信息储存在另一处; 有些系统在文件中创建一份文件描述。 在处理文件方面, 有些系统使用单个换行符标记行末尾, 而其他系统可能使用回车符和换行符的组合来表示行末尾。有些系统用最小字节来衡量文件的大小, 有些系统则以字节块的大小来衡量。

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

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

文件结尾

计算机操作系统要以某种方式判断文件的开始和结束。 检测文件结尾的一种方法是, 在文件末尾放一个特殊的字符标记文件结尾。

操作系统使用的另一种方法是储存文件大小的信息。 如果文件有3000字节, 程序在读到3000字节时便达到文件的末尾。 MS-DOS 及其相关系统使用这种方法处理二进制文件, 因为用这种方法可以在文件中储存所有的字符,包括Ctrl+Z。 新版的DOS也使用这种方法处理文本文件。 UNIX使用这种方法处理所有的文件。

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

#define EOF (-1)

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

定义的值一定与输入字符所产生的返回值不同。 如果包含stdio.h文件, 并使用EOF符号, 就不必担心EOF值不同的问题。

变量ch的类型从char变为int, 因为char类型的变量只能表示0~255的无符号整数, 但是EOF的值是-1。 还好, getchar()函数实际返回值的类型是int, 所以它可以读取EOF字符。 如果实现使用有符号的char类型, 也可以把ch声明为char类型, 但最好还是用更通用的形式。

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

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

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

模拟EOF和图形界面模拟EOF的概念是在使用文本界面的命令行环境中产生的。用户通过击键与程序交互, 由操作系统生成EOF信号。

重定向和文件

在默认情况下, C程序使用标准I/O包查找标准输入作为输入源。stdin流, 它是把数据读入计算机的常用方式。 它可以是一个过时的设备, 如磁带、 穿孔卡或电传打印机, 或者(假设) 是键盘, 甚至是一些先进技术, 如语音输入。

程序可以通过两种方式使用文件。 第 1 种方法是, 显式使用特定的函数打开文件、 关闭文件、 读取文件、 写入文件, 诸如此类。第2种方法是, 设计能与键盘和屏幕互动的程序, 通过不同的渠道重定向输入至文件和从文件输出。

重定向的一个主要问题与操作系统有关, 与C无关。 尽管如此, 许多C环境中(包括UNIX、 Linux和Windows命令提示模式) 都有重定向特性, 而且一些C实现还在某些缺乏重定向特性的系统中模拟它

UNIX、 Linux和DOS重定向

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

重定向输入

文本文件(text file) 是内含文本的文件, 其中储存的数据是我们可识别的字符。 文件的内容可以是一篇散文或者C程序。 内含机器语言指令的文件(如储存可执行程序的文件) 不是文本文件。 由于该程序的操作对象是字符, 所以要使用文本文件。

<符号是UNIX和DOS/Windows的重定向运算符。 该运算符使words文件与stdin流相关联, 把文件中的内容导入echo_eof程序。 echo_eof程序本身并不知道(或不关心) 输入的内容是来自文件还是键盘, 它只知道这是需要导入的字符流, 所以它读取这些内容并把字符逐个打印在屏幕上, 直至读到文件结尾。

重定向

对于UNIX、 Linux和Windows命令提示, <两侧的空格是可选的。 一些系统, 如AmigaDOS(那些喜欢怀旧的人使用的系统) , 支持重定向, 但是在重定向符号和文件名之间不允许有空格。

重定向输出

echo_eof>mywords

>符号是第2个重定向运算符。 它创建了一个名为mywords的新文件, 然后把echo_eof的输出(即, 你输入字符的副本) 重定向至该文件中。 重定向把stdout从显示设备(即, 显示器) 赋给mywords文件。

组合重定向

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

重定向运算符连接一个可执行程序(包括标准操作系统命令) 和一个数据文件, 不能用于连接一个数据文件和另一个数据文件, 也不能用于连接一个程序和另一个程序。

重定向运算符连接一个可执行程序(包括标准操作系统命令) 和一个数据文件, 不能用于连接一个数据文件和另一个数据文件, 也不能用于连接一个程序和另一个程序。

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

重定位让你能使用键盘输入程序文件。重定向是一个命令行概念, 因为我们要在命令行输入特殊的符号发出指令。

如何重定向输入和输出

绝大部分C系统都可以使用重定向, 可以通过操作系统重定向所有程序, 或只在C编译器允许的情况下重定向C程序。 假设prog是可执行程序名, file1和file2是文件名。

把输出重定向至文件: >

prog >file1

把输入重定向至文件: <

prog

组合重定向:

prog file1

prog >file1

一些系统要求重定向运算符左侧有一个空格, 右侧没有空格。 而其他系统(如, UNIX) 允许在重定位运算符两侧有空格或没有空格。

创建更友好的用户文件

C提供了大量工具让输入更顺畅, 处理过程更顺利。

使用缓冲输入

缓冲输入用起来比较方便, 因为在把输入发送给程序之前, 用户可以编辑输入。

使用while循环丢弃输入行最后剩余的内容, 包括换行符。 这种方法的优点是, 能把no和no way这样的响应视为简单的n。

混合数值和字符输入

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

该程序以 int 类型读取字符(这样做可以检测 EOF) , 但是却以char 类型把字符传递给display()函数。 因为char比int小, 一些编译器会给出类型转换的警告。

这次是输入行中紧跟在 3 后面的换行符。 scanf()函数把这个换行符留在输入队列中。 和 scanf()不同, getchar()不会跳过换行符, 所以在进入下一轮迭代时, 你还没来得及输入字符, 它就读取了换行符, 然后将其赋给ch。 而ch是换行符正式终止循环的条件。

程序要跳过一轮输入结束与下一轮输入开始之间的所有换行符或空格。 另外, 如果该程序不在getchar()测试时, 而在scanf()阶段终止程序会更好。

while循环实现了丢弃scanf()输入后面所有字符(包括换行符) 的功能,为循环的下一轮读取做好了准备

在if语句中使用一个break语句, 可以在scanf()的返回值不等于2时终止程序, 即如果一个或两个输入值不是整数或者遇到文件结尾就终止程序。

输入验证

假设你编写了一个处理非负数整数的循环, 但是用户很可能输入一个负数。

另一类潜在的陷阱是, 用户可能输入错误类型的值, 如字符 q。 排除这种情况的一种方法是, 检查scanf()的返回值。

算术运算的函数提供整数, 该函数计算特定范围内所有整数的平方和。 程序限制了范围的上限是10000000, 下限是-10000000。

你可能感兴趣的:(2022-11-12字符输入/输出和输入验证)