C++的I/O流中若干使用技巧的探讨

时间:2022-07-02 08:26:56

摘要:在使用安全、方便的c++的I/O流操作中,某些细节的处理往往被忽视,这会给看似简单的程序带来料想不到的影响。文章结合作者多年c语言及c++语言的教学经验,针对I/O流在教学过程中遇到的问题,对缓冲式输入、输出操作进行了较为深入的研究,提出了若干使用技巧,希望能给c++教学和c++语言的运用提供一些有益的借鉴。

关键词:C++;I/O流;缓存;eof函数

0引言

与c语言不同,c++可使用类型安全的I/O操作。插入操作符“”被重载以便能接受特定类型的数据。如果实际的数据类型和函数不匹配,则会终止程序执行。如果需要处理非法的数据类型,则用户可以通过流中设置的不同错误标志来测试输入输出的成功与否。因此,c++的输入输出较c语言更加安全。另一方面,插入和提取操作都能自动识别其插入或提取数据的类型,所以比c语言要按指定格式输入输出数据类型要方便许多。但正因为其安全、方便,在使用中对一些细节往往关注不够。在教学过程中发现,学生中许多简单的、看似毫无问题的程序经常会产生意想不到的结果,这种情况往往会使学生和使用者动摇对c++稳定性的信任。下面从一些具体问题出发分析其产生的根源并探讨解决办法。

1 COUt的输出缓存问题

1.1交互式输入输出顺序问题

在交互式的应用程序中,常常希望在屏幕上出现提示信息后,用户再键入相应的数据作出响应。即,程序在处理输入操作前先要显示提示信息。但在使用不当的情况下,这种交互的效果不但达不到,反而会出现令人费解的现象,先来看下面程序l所示的简单例子。

运行程序1后发现,程序并未如设想的那样,先输出提示信息,后再接受输入;而是在输入数据之后才输出此前的那条提示信息。为什么输出顺序颠倒呢?其根源在于c++的I/0流内部带有缓冲区,而cout就是一个I/0流ostream流类的一个标准的输出对象。在有输出缓存的情况下,输出并不都是即时的。只有当输出缓存已满、程序明确要求、或程序结束时,输出缓存的信息才会显示出来。

下面,换一种输入方式,将程序l中的输入语句改为用cin实现,如程序2所示。运行该程序的结果是输出顺序不再是程序1的颠倒顺序,而是正常的顺序了。原来,c++提供了成员函数tie来同步istream和ostream的操作,以保证输出在输入之前显示出来。即调用语句cin.tie(&cout);

可以把cout连接到cin。事实上,无须显式地在代码中使用此调用语句,c++会自动执行该调用来创建用户的标准输入/输出环境。这就是为什么程序2能得以正常执行的原因。

那么如何使程序l中的cout在没有和getchar()输入“捆绑”在一起的前提下,也能按正确的顺序输入输出,也就是说无论输出缓存是否已满,都能即时输出,这就需要通过手动刷新缓冲区的方式来强制要求输出缓存的信息。具体可以用流操作符endl或者flush来清空输出缓存,以达到即时输出的目的。见程序3。

1.2 cout中多表达式的输出顺序问题

cout允许其后插入多个表达式,但在许多c++系统中,多个表达式之间的求值顺序却出乎意料。观察程序4的运行结果,我们看到,程序运行结果不是预想的:

f1

1

f2

2而是

f2

f1

1

2

如果将主函数改为如程序5所示的形式,则程序运行结果为:

1

2

f2

f1

可以看出,程序5先输出了prinff的结果,然后再输出cout的结果。而按程序本身的顺序,应该是cout的执行在前,返回给prinff的结果在后。这也印证了cout在使用上要注意输出缓存的问题。另一方面f2,f1的输出顺序与程序4一样,都和预想的相反。其原因是,在许多c++系统中,无论是在cout的“

2 cin的输入缓存问题

通过标准输入流cin输入数据时,提取符“

运行程序6却发现,在输入非法数据的时候,程序并不能重新接受数据,而是陷入了死循环。可见通过clear()函数将流的标记更改为正确还不够,原因是cin是类型敏感的输入,对非法类型的数据是不提取的;同时cin又是缓冲式输入,不被提取的非法类型数据便一直留在缓冲区内。这时只有通过get()成员函数清除掉缓冲区的非法类型数据后,cin才能重新提取正确的数据并送入变量,否则将会陷入死循环。所以,上述程序必须将被注释掉的那条cin.getO语句变为有效代码才能达到目的。

3 eof函数的判定时间问题

许多地方对eofO函数的解释都是,“判定是否已经读到文件的结尾,如果到文件结尾,该函数返回值为1,否则返回为0”。但在程序中使用该函数时常常会感到困惑,如程序7要实现的功能是将文本文件"a.txt"的内容输出到屏幕上。(假定文本文件的内容是连续存放的26个小写英文字母)

程序运行情况却是,在屏幕上输出26个小写英文字母之后,又多输出了一个“z”,即最后一个字符输出了两次。这说明当文件指针到达文件末尾时,执行eof并不会返回1,而是要到下一次读取后才会返回1。

事实上,文件本身是没有文件结束符EOF的。当读取文件中最后一个有效字符后,虽然文件指针已指向空白了,但这时还不知道是否到了文件末尾,只有再读取一次文件,待读不到任何内容了,这时输入流设置eofbit位,eof的返回值才为l,而空的内容是不会被提取到变量的,故最后一次读到变量中的内容又被重复输出了。避免多输出一次的错误可采用如程序8的先读取后判断的方法。

4结束语

综上所述,由于c++的标准输入输出流是带缓冲的输入输出,使用中需注意以下问题。

(1)cout与cin之间的同步操作由系统自动执行,但与其它输入方式之间交互的正确性则需手动刷新缓冲区的方法来保证。

(2)在多表达式输出中,不要将相互有值依赖关系的表达式放到一个cout语句中。

(3)同一程序中尽量不要将带缓冲的cout和不带缓冲的prinff这两种输出混用。

(4)cin不提取非法类型的数据,若要处理非法数据,必须借助其它输入方式清掉输入缓存中的非法数据。

(5)用eof函数判所读取文件结束与否时,宜采用先读取后判断的步骤。

以上这些细节的处理常常是被忽视的,但这种忽视往往又会给看似简单的程序带来意想不到的影响,所以希望在教学及应用中能够对这些问题的处理引起足够的重视。

(注:本文中所涉及到的图表、注解、公式等内容请以PDF格式阅读原文。)

上一篇:USSD应用接口协议的分层实现 下一篇:医学高专(高职)计算机课程教学改革的探索