预计阅读本页时间:-
很多人都曾经编写过难以使用的程序。幸运的是,C赋予您的一些工具可以使输入变成更顺利且更令人愉快的过程。不幸的是,学习这些工具最开始会引发新的问题。本节的目标就是指导您克服这样的一些问题而获得一个更友好的用户界面,这样的界面使交互式的数据输入更轻松,并减轻了错误输入的影响。
8.5.1 使用缓冲输入
缓冲输入通常会给用户带来方便,它提供了在将输入发送至程序前对其进行编辑的机会,但在使用字符输入时这会给编程人员带来麻烦。正如您在前面的一些例子中所看到的,问题在于缓冲输入需要您按下回车键来提交您的输入。这一动作还传输一个程序必须处理的换行符。我们用一个猜测程序来研究这个问题及其相关问题。您选择一个数,计算机尝试猜测该数。我们使用的是一种很慢的算法,但我们着重考虑的是I/O而不是算法。程序清单8.4为该程序的初始版本。
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
程序清单8.4 guess.c程序
下面是一个运行示例:
该程序使用的猜测算法很傻,我们不去考虑它。我们选择一个很小的数。注意该程序在每次您输入n时都进行两次猜测。这中间所发生的事情是程序读取n响应并把它看作是您对1的否定,然后读取换行字符并把它看作是您对2的否定。
一种解决方案是使用一个while循环来丢弃输入行的其余部分,包括换行符。这种处理方法还能够把诸如no和no way这样的响应与简单的n响应一样看待。程序清单8.4中的版本将no作为两个响应。下面是解决该问题的一个修改过的循环。
使用这个循环产生如下面所示的响应:
这就解决了换行符的问题。然而,作为完美主义者,您可能还不希望程序将f的意义看作与n相同。要改正这一缺点,您可以使用一个if语句来筛选掉其他响应。首先,添加一个char变量来存储响应:
然后,将循环改为如下形式:
现在该程序的响应如下所示:
当您编写交互式程序时,您应该试着去预料用户未能遵循指示的可能方式。然后您应该将程序设计为得体地处理用户的疏忽。告诉用户哪里出现了错误,并给予他们另一次机会。
当然,您应该向用户提供清晰的指示。但不论您提供的指示如何清晰,一些人总是会曲解它们,然后责怪您的指示不够详细。
8.5.2 混合输入数字和字符
假设您的程序同时需要使用getchar()进行字符输入和使用scanf()进行数字输入。这两个函数中的每一个都能很好地完成其工作,但它们不能很好地混合在一起。这是因为getchar()读取每个字符,包括空格、制表符和换行符;而scanf O在读取数字时则会跳过空格、制表符和换行符。
为了举例说明它所产生的问题,程序清单8.5给出了一个程序,该程序读取一个字符和两个数作为输入,然后使用由所输入的两个数字指定的行数和列数来打印该字符。
程序清单8.5 showcharl.c程序
请注意该程序将字符读取为int类型以进行EOF检测。然而,它将该字符作为char类型传给display()函数。因为char比int小,所以一些编译器会对这一转换提出警告。在本例中,您可以忽略这一警告。
程序的结构是由main()获取数据,由display()函数进行打印。我们来看一个运行示例以发现问题是什么。
该程序开始时表现很好。您输入c 2 3,程序就如期打印2行c字符,每行3个。然后该程序提示输入第二组数据,并在您还没能做出响应之前就退出了!哪里错了呢?又是换行符,这次是紧跟在第一个输入行的3后面的那个换行符。scanf()函数将该换行符留在了输入队列中。与scanf()不同,getchar()并不跳过换行符。所以在循环的下一个周期,在您有机会输入任何其他内容之前,这一换行符由getchar()读出,然后将其赋值给ch,而ch为换行符正是终止循环的条件。
要解决这一问题,该程序必须跳过一个输入周期中键入的最后一个数字与下一行开始处键入的字符之间的所有换行符或空格。另外,如果除了getchar()判断之外还可以在scanf()阶段终止该程序,则会更好。程序清单8.6中显示的另一版本中实现了这些功能。
程序清单8.6 showchar2.c程序
while语句使程序剔除scanf()输入后的所有字符,包括换行符。这样就让循环准备好读取下一行开始的第一个字符。这意味着您可以自由地输入数据:
通过使用一个if语句和一个break,如果scanf()的返回值不是2,您就中止了该程序。这种情况在有一个或两个输入值不是整数或者遇到文件尾时发生。