第8章 字符输入/输出和输入验证

news2024/11/17 12:28:10

本章介绍以下内容:
更详细地介绍输入、输出以及缓冲输入和无缓冲输入的区别
如何通过键盘模拟文件结尾条件
如何使用重定向把程序和文件相连接
创建更友好的用户界面
在涉及计算机的话题时,我们经常会提到输入(input)和输出(output)。我们谈论输入和输出设备(如键盘、U盘、扫描仪和激光打印机),讲解如何处理输入数据和输出数据,讨论执行输入和输出任务的函数。本章主要介绍用于输入和输出的函数(简称I/O函数)。
I/O函数(如printf()、scanf()、getchar()、putchar()等)负责把信息传送到程序中。前几章简单介绍过这些函数,本章将详细介绍它们的基本概念。同时,还会介绍如何设计与用户交互的界面。
最初,输入/输出函数不是C定义的一部分,C把开发这些函数的任务留给编译器的实现者来完成。在实际应用中,UNIX 系统中的 C 实现为这些函数提供了一个模型。ANSI C 库吸取成功的经验,把大量的UNIX I/O函数囊括其中,包括一些我们曾经用过的。由于必须保证这些标准函数在不同的计算机环境中能正常工作,所以它们很少使用某些特殊系统才有的特性。因此,许多C供应商会利用硬件的特性,额外提供一些I/O函数。其他函数或函数系列需要特殊的操作系统支持,如Winsows或Macintosh OS提供的特殊图形界面。这些有针对性、非标准的函数让程序员能更有效地使用特定计算机编写程序。本章只着重讲解所有系统都通用的标准 I/O 函数,用这些函数编写的可移植程序很容易从一个系统移植到另一个系统。处理文件输入/输出的程序也可以使用这些函数。
许多程序都有输入验证,即判断用户的输入是否与程序期望的输入匹配。本章将演示一些与输入验证相关的问题和解决方案。

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

8.2 缓冲区

如果在老式系统运行程序清单8.1,你输入文本时可能显示如下:
HHeelllloo,, tthheerree..II wwoouulldd[enter]

像这样回显用户输入的字符后立即重复打印该字符是属于无缓冲(或直接)输入,即正在等待的程序可立即使用输入的字符。对于该例,大部分系统在用户按下Enter键之前不会重复打印刚输入的字符,这种输入形式属于缓冲输入。用户输入的字符被收集并储存在一个被称为缓冲区(buffer)的临时存储区,按下Enter键后,程序才可使用用户输入的字符

 图8.1 缓冲输入和无缓冲输入

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

虽然缓冲输入好处很多,但是某些交互式程序也需要无缓冲输入。例如,在游戏中,你希望按下一个键就执行相应的指令。因此,缓冲输入和无缓冲输入都有用武之地

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

ANSI C和后续的C标准都规定输入是缓冲的,不过最初K&R把这个决定权交给了编译器的编写者

ANSI C决定把缓冲输入作为标准的原因是:一些计算机不允许无缓冲输入

ANSI没有提供调用无缓冲输入的标准方式,这意味着是否能进行无缓冲输入取决于计算机系统

本书假设所有的输入都是缓冲输入

8.3 结束键盘输入

8.3.1 文件、流和键盘输入

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

从较低层面上,C可以使用主机操作系统的基本文件工具直接处理文件,这些直接调用操作系统的函数被称为底层 I/O (low-level I/O)。由于计算机系统各不相同,所以不可能为普通的底层I/O函数创建标准库,ANSI C也不打算这样做。然而从较高层面上,C还可以通过标准I/O包(standard I/O package)来处理文件。这涉及创建用于处理文件的标准模型和一套标准I/O函数。在这一层面上,具体的C实现负责处理不同系统的差异,以便用户使用统一的界面

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

C程序处理的是流而不是直接处理文件。流(stream)是一个实际输入或输出映射的理想化数据流

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

C 的输入函数内置了文件结尾检测器。既然可以把键盘输入视为文件,那么也应该能使用文件结尾检测器结束键盘输入

8.3.2 文件结尾

检测文件结尾的一种方法是,在文件末尾放一个特殊的字符标记文件结尾

操作系统使用的另一种方法是储存文件大小的信息。如果文件有3000字节,程序在读到3000字节时便达到文件的末尾

无论操作系统实际使用何种方法检测文件结尾,在C语言中,用getchar()读取文件检测到文件结尾时将返回一个特殊的值,即EOF(end of file的缩写)。scanf()函数检测到文件结尾时也返回EOF。通常, EOF定义在stdio.h文件中:
#define EOF (-1)

无论哪种情况,-1都不对应任何字符,所以,该值可用于标记文件结尾

某些系统也许把EOF定义为-1以外的值,但是定义的值一定与输入字符所产生的返回值不同。如果包含stdio.h文件,并使用EOF符号,就不必担心EOF值不同的问题。这里关键要理解EOF是一个值,标志着检测到文件结尾,并不是在文件中找得到的符号

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

如果正在读取的是键盘输入不是文件会怎样?绝大部分系统(不是全部)都有办法通过键盘模拟文件结尾条件

正确的方法是,必须找出当前系统的要求。例如,在大多数UNIX和Linux系统中,在一行开始处按下Ctrl+D会传输文件结尾信号。许多微型计算机系统都把一行开始处的Ctrl+Z识别为文件结尾信号,一些系统把任意位置的Ctrl+Z解释成文件结尾信号

8.4 重定向和文件

在默认情况下,C程序使用标准I/O包查找标准输入作为输入源。这就是前面介绍过的stdin流

程序可以通过两种方式使用文件。第 1 种方法是,显式使用特定的函数打开文件、关闭文件、读取文件、写入文件,诸如此类

第2种方法是,设计能与键盘和屏幕互动的程序,通过不同的渠道重定向输入至文件和从文件输出。换言之,把stdin流重新赋给文件。继续使用getchar()函数从输入流中获取数据

8.4.1 UNIX、Linux和DOS重定向

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

1.重定向输入

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

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

2.重定向输出

echo_eof>mywords
>符号是第2个重定向运算符。它创建了一个名为mywords的新文件,然后把echo_eof的输出(即,你输入字符的副本)重定向至该文件中。重定向把stdout从显示设备(即,显示器)赋给mywords文件。如果已经有一个名为mywords的文件,通常会擦除该文件的内容,然后替换新的内容(但是,许多操作系统有保护现有文件的选项,使其成为只读文件)。所有出现在屏幕的字母都是你刚才输入的,其副本储存在文件中。在下一行的开始处按下Ctrl+D(UNIX)或Ctrl+Z(DOS)即可结束该程序。如果不知道输入什么内容,可参照下面的示例。这里,我们使用UNIX提示符$。记住在每行的末尾单击Enter键,这样才能把缓冲区的内容发送给程序

3.组合重定向

现在,假设你希望制作一份mywords文件的副本,并命名为savewords。只需输入以下命令即可:
echo_eof < mywords > savewords
下面的命令也起作用,因为命令与重定向运算符的顺序无关:
echo_eof > savewords < mywords
注意:在一条命令中,输入文件名和输出文件名不能相同

在UNIX、Linux或Windows/DOS系统中使用两个重定向运算符(<和>)时,要遵循以下原则。
重定向运算符连接一个可执行程序(包括标准操作系统命令)和一个数据文件,不能用于连接一个数据文件和另一个数据文件,也不能用于连接一个程序和另一个程序

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

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

UNIX、Linux或Windows/DOS 还有>>运算符,该运算符可以把数据添加到现有文件的末尾,而 | 运算符能把一个文件的输出连接到另一个文件的输入。欲了解所有相关运算符的内容,请参阅 UNIX 的相关书籍,如UNIX Primer Plus,Third Edition(Wilson、Pierce和Wessler合著)

4.注释

如果用不了重定向,可以用程序直接打开文件

小结:如何重定向输入和输出
绝大部分C系统都可以使用重定向,可以通过操作系统重定向所有程序,或只在C编译器允许的情况下重定向C程序。假设prog是可执行程序名,file1和file2是文件名。
把输出重定向至文件:>
prog >file1
把输入重定向至文件:<
prog <file2
组合重定向:
prog <file2 >file1
prog >file1 <file2
这两种形式都是把file2作为输入、file1作为输出。
留白:
一些系统要求重定向运算符左侧有一个空格,右侧没有空格。而其他系统(如,UNIX)允许在重定位运算符两侧有空格或没有空格。

8.5 创建更友好的用户界面

8.5.1 使用缓冲输入

一种解决方案是,使用while循环丢弃输入行最后剩余的内容,包括换行符。这种方法的优点是,能把no和no way这样的响应视为简单的n。程序清单8.4的版本会把no当作两个响应。下面用循环修正
char response;这个问题:
while (getchar() != 'y')  /* 获取响应,与 y 做对比*/
{
printf("Well, then, is it %d?\n", ++guess);
while (getchar() != '\n')
continue;     /* 跳过剩余的输入行 */
}

这的确是解决了换行符的问题。但是,该程序还是会把f被视为n。我们用if语句筛选其他响应。首先,添加一个char类型的变量储存响应:
修改后的循环如下:
while ((response = getchar()) != 'y') /* 获取响应 */
{
if (response == 'n')
printf("Well, then, is it %d?\n", ++guess);
else
printf("Sorry, I under-
stand only y or n.\n");
while (getchar() != '\n')
continue; /* 跳过剩余的输入行 */
}

8.5.2 混合数值和字符输入

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

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

8.6 输入验证

作为程序员,除了完成编程的本职工作,还要事先预料一些可能的输入错误,这样才能编写出能检测并处理这些问题的程序

下面的表达式当且仅当用户输入一个整数时才为真:
scanf("%ld", &n) == 1
结合上面的while循环,可改进为:
long n;
while (scanf("%ld", &n) == 1 && n >= 0)
{
//处理n
}

8.6.1 分析程序

8.6.2 输入流和数字

输入由字符组成,但是scanf()可以把输入转换成整数值或浮点数值。使用转换说明(如%d或%f)限制了可接受输入的字符类型,而getchar()和使用%c的scanf()接受所有的字符

8.7 菜单浏览

8.7.1 任务

一个菜单程序需要执行哪些任务。它要获取用户的响应,根据响应选择要执行的动作。另外,程序应该提供返回菜单的选项。C 的 switch 语句是根据选项决定行为的好工具,用户的每个选择都可以对应一个特定的case标签。使用while语句可以实现重复访问菜单的功能
获取选项
当选项不是'q'时
转至相应的选项并执行
获取下一个选项

8.7.2 使执行更顺利

显示选项
获取用户的响应
当响应不合适时
提示用户再次输入
获取用户的响应

char get_choice(void)
{
int ch;
printf("Enter the letter of your choice:\n");
printf("a. advice         b. bell\n");
printf("c. count          q. quit\n");
ch = get_first();
while ((ch < 'a' || ch > 'c') && ch != 'q')
{
printf("Please respond with a, b, c, or q.\n");
ch = getfirst();
}
return ch;
}
char get_first(void)
{
int ch;
ch = getchar();  /* 读取下一个字符 */
while (getchar() != '\n')
continue; /* 跳过该行剩下的内容 */
return ch;
}

8.7.3 混合字符和数值输入

重写 get_first(),使其返回下一个非空白字符而不仅仅是下一个字符,即可修复这个问题。我们把这个任务留给读者作为练习。另一种方法是,在count()函数中清理换行符

8.8 关键概念

8.9 本章小结

许多程序使用 getchar()逐字符读取输入。通常,系统使用行缓冲输入,即当用户按下 Enter 键后输入才被传送给程序。按下Enter键也传送了一个换行符,编程时要注意处理这个换行符。ANSI C把缓冲输入作为标准。
通过标准I/O包中的一系列函数,以统一的方式处理不同系统中的不同文件形式,是C语言的特性之一。getchar()和 scanf()函数也属于这一系列。当检测到文件结尾时,这两个函数都返回 EOF(被定义在stdio.h头文件中)。在不同系统中模拟文件结尾条件的方式稍有不同。在UNIX系统中,在一行开始处按下Ctrl+D可以模拟文件结尾条件;而在DOS系统中则使用Ctrl+Z。
许多操作系统(包括UNIX和DOS)都有重定向的特性,因此可以用文件代替键盘和屏幕进行输入和输出。读到EOF即停止读取的程序可用于键盘输入和模拟文件结尾信号,或者用于重定向文件。
混合使用 getchar()和 scanf()时,如果在调用 getchar()之前,scanf()在输入行留下一个换行符,会导致一些问题。不过,意识到这个问题就可以在程序中妥善处理。
编写程序时,要认真设计用户界面。事先预料一些用户可能会犯的错误,然后设计程序妥善处理这些错误情况。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/956783.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Facebook登录SDK

一、Facebook SDK接入 官方文档&#xff1a;https://developers.facebook.com/docs/facebook-login/android 按照流程填写完成 1、选择新建应用 如果已经创建了应用就点【搜索你的应用】&#xff0c;忽略2、3步骤 2、选择【允许用户用自己的Facebook账户登录】 3、填写应用…

简析vue文件编译——AST

简介 首先了解一个概念AST&#xff08;abstract syntax tree&#xff09;抽象语法树&#xff0c;按照大多数教程中的描述&#xff0c;这是一种源代码的抽象语法结构树&#xff0c;树上的每个节点都表示源代码中的一种结构&#xff0c;将源码中的各种嵌套括号等形式&#xff0c…

Android安卓实战项目(13)---记账APP详细记录每天的收入和支出并且分类统计【生活助手类APP】强烈推荐自己也在用!!!(源码在文末)

Android安卓实战项目&#xff08;13&#xff09;—记账APP详细记录每天的收入和支出并且分类统计【生活助手类APP】强烈推荐自己也在用&#xff01;&#xff01;&#xff01;&#xff08;源码在文末&#x1f415;&#x1f415;&#x1f415;&#xff09; 一.项目运行介绍 B站…

antd实现年日输入框联动

效果: 1、默认显示年&#xff0c;日期区间默认显示今年2023——2024 年份显示前5年后5年 2、如果选择了月份&#xff0c;日期区间显示从1月份到12月份 部分代码: (react 使用class类组件)

Lee滤波python实现(还包括frost等滤波)

Lee滤波按定义实现&#xff1a; from scipy.ndimage.filters import uniform_filter from scipy.ndimage.measurements import variancedef lee_filter(img, size):img_mean uniform_filter(img, (size, size))img_sqr_mean uniform_filter(img**2, (size, size))img_varian…

最小生成树 -prim算法

一般无向图建图稠密图-prim算法稀疏图-kruskal算法 prim : 加点法 1.先随机选一个点&#xff0c;加入集合 &#xff0c;之后寻找最短的距离的点加入集合&#xff0c;行程最小生成树。 2.注意最小生成树是不能有回路的&#xff0c; 所以可以把回路设置成最大值&#xff0c;即假装…

idea使用maven时的java.lang.IllegalArgumentException: Malformed \uxxxx encoding问题解决

idea使用maven时的java.lang.IllegalArgumentException: Malformed \uxxxx encoding问题解决 欢迎使用Markdown编辑器1、使用maven clean install -X会提示报错日志2、在Poperties.java文件的这一行打上断点3、maven debug进行调试4、运行到断点位置后&#xff0c;查看报错char…

贝锐蒲公英异地组网方案,如何阻断网络安全威胁?

随着混合云和移动办公的普及&#xff0c;企业网络面临着越来越复杂的安全威胁环境。 大型企业有足够的能力和预算&#xff0c;构建覆盖全部个性化需求的定制化网络安全方案。 但对于广大中小企业来说&#xff0c;由于实际业务发展情况&#xff0c;他们难以在部署周期、预算成本…

Redis数据结构应用场景及原理分析

目录 一、Redis介绍 二、应用场景 2.1 String应用场景 2.2 Hash应用场景 2.3 List应用场景 2.4 Set应用场景 2.5 Zset应用场景 一、Redis介绍 单线程多路复用底层数据结构&#xff1a;全局哈希表&#xff08;key-value&#xff09; 二、应用场景 2.1 String应用…

安装centos7修改网关时出现ifconfig命令找不到的解决方法

系列文章专栏 学习以来遇到的bug/问题专栏 文章目录 系列文章专栏 一 问题描述 二 解决方法 2.1 原因分析 前言 本文主要介绍安装centos7修改网关时出现ifconfig命令找不到的解决方法 一 问题描述 安装centos7修改网关时出现ifconfig命令找不到的情况 二 解决方法 2…

Revit SDK:SolidSolidCut 实体几何裁剪

前言 这个例子介绍了 Revit 中的一个实体几何裁剪。 内容 这个例子介绍如何使用 SolidSolidCutUtils 的接口来做几何裁剪以及取消几何裁剪。内容相对来说非常简单。 namespace Autodesk.Revit.DB {public static class SolidSolidCutUtils{public static void AddCutBetwee…

vue3组合式api bus总线式通信

vue2中可以创建一个 vue 实例&#xff0c; 做为 总结来完成组件间的通信 但是在vue3中&#xff0c; 这种方法是不能使用的。 因为vue3中main.js中&#xff0c; 使用的createApp() 没有机会再写 new Vue了 但是我们可以使用 mitt 的插件来解决这个问题 vue3 bus组件的用法 安装…

Mysql表关联简单介绍(inner join、left join、right join、full join不支持、笛卡尔积)

文章目录 0. 交集、并集、差集含义说明1. 简单演示上图七种情况0. A、B表数据准备1. left outer join 简称 left join 左表所有数据&#xff0c;右表关联数据&#xff0c;没有的以null填充2. right outer join 简称 right join&#xff0c;右表所有数据&#xff0c;左表关联数据…

【SpringCloud】SpringCloud整合openFeign

文章目录 前言1. 问题分析2. 了解Feign3. 项目整合Feign3.1 引入依赖3.2 添加注解3.3 编写Feign客户端3.4 测试3.5 总结 4. 自定义配置4.1 配置文件方式4.2 Java代码方式 5. Feign使用优化5.1 引入依赖5.2 配置连接池 6. Feign最佳实践6.1 继承方式6.2 抽取方式 前言 微服务远…

报错处理:Too many open files

报错处理 Too many open files 报错环境 Linux 排错思路 当打开的文件句柄超过系统允许的最大值时&#xff0c;会出现该错误。这可能是由于系统参数限制或者应用程序打开了过多的文件导致的。 解决方法 可以通过修改系统参数来增加最大允许打开文件句柄数。 临时性修改&#xf…

ssm农业视频实时发布管理系统源码

ssm农业视频实时发布管理系统源码108 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm package com.controller;import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; impo…

ui网页设计实训心得

ui网页设计实训心得篇一 通过这次实训对这门课程的学习&#xff0c;做好网页&#xff0c;并不是一件容易的事&#xff0c;它包括网页的选题、 内容采集整理、 图片的处理、 页面的排版设置、 背景及其整套网页的色调等很多东西。 所以我得出一下总结&#xff1a; 一、 准备资…

将本地jar打包到本地maven仓库或maven私服仓库中

将本地jar包打包到本地的maven仓库中的命令&#xff1a; mvn install:install-file -DgroupIdtebie.applib.api -DartifactIdapiclient -Dversion1.0-SNAPSHOT -Dfile本地jar路径 -Dpackagingjar说明&#xff1a; DgroupId pom中的<groupId></groupId> Dartifact…

【爬虫】实验项目二:模拟登录和数据持久化

目录 一、实验目的 二、实验预习提示 三、实验内容 实验要求 基本要求&#xff1a; 改进要求A&#xff1a; 改进要求B&#xff1a; 四、实验过程 基本要求&#xff1a; 源码如下&#xff1a; 改进要求A: 源码如下&#xff1a; 改进要求B&#xff1a; 源码如下&…

deepspeed多机多卡并行训练指南

文章目录 前言离线配置训练环境共享文件系统多台服务器之间配置互相免密登录pdsh多卡训练可能会碰到的问题注意总结 前言 我的配置&#xff1a; 7机14卡&#xff0c;每台服务器两张A800 问&#xff1a;为啥每台机只挂两张卡&#xff1f; 答&#xff1a;给我的就这样的&#…