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

来源 :计算机时代 | 被引量 : 0次 | 上传用户:skycat
下载到本地 , 更方便阅读
声明 : 本文档内容版权归属内容提供方 , 如果您对本文有版权争议 , 可与客服联系进行内容授权或下架
论文部分内容阅读
  摘要:在使用安全、方便的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的“<<”运算中,还是prinff的表达式求值中,多表达式的求值顺序都是自右向左进行的。所以在程序4里cout的多表达式输出中,表达式送入输出缓冲区的顺序是自左向右的,而表达式的求值顺序则是自右向左进行的。因此,在多表达式输出中,不要将相互有值依赖的表达式放到一个cout语句中,也尽量不要将带缓冲的cout和不带缓冲的prinff这两种输出混用,否则会带来不可预料的结果。
  
  2 cin的输入缓存问题
  
  通过标准输入流cin输入数据时,提取符“<<”能自动识别其后的数据类型,所以一旦输入的数据类型与规定的类型不相匹配,流提取操作符就会设置流的failbit状态位,输入的数据就不会被提取,从而保证了输入的安全性。但有时程序需要对非法类型的数据进行处理,如在用cin对自定义类型进行操作时,当发现输入有错时需要予以纠正,以便重新输入,这时就需要使用clear()函数将流的标记更改为正确,如程序6所示。
  运行程序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格式阅读原文。)
其他文献
摘要:利用Authorware外部扩展函数,对Authorware中的菜单进行改进,在Authorware中实现了多级菜单和右键弹出菜单。  关键词:Authorware;外部扩展函数;多级菜单;弹出菜单    0引言    Authorware提供了"Pull-Down Menu”这种交互方式,利用它可以制作出菜单。不过,与常见的多级Windows标准菜单相比,它的菜单大为逊色,而右键弹出菜单就
期刊
摘要:分布式拒绝服务(DDoS)攻击是目前黑客经常采用而难以防范的攻击手段。文章从DDoS的概念、攻击原理、攻击过程、攻击方法四个方面对DDoS加以说明。最后,介绍了一个局域网内的攻击实例。  关键词:DDoS;TCP/IP;TCP连接洪水;TCP SYN洪水;Smurf攻击    0引言    DDoS(分布式拒绝服务),其全称为Distributed Denial ofService,它是一种
期刊
摘要:SIP协议是应用层会话控制协议,具有简单、可扩展和分布式的特点。LDAP目录作为一种非关系型数据库,可以简化查询的步骤,PAM就是在应用程序和下层的认证模块之间加入一个抽象层,使得上层应用和底层认证相互独立。文章提出了通过LDAP目录与PAM结合来实现SIP服务器的认证方案,并利用pam_ldap模块实现了SIP服务器的LDAP认证。  关键词:SIP;PAM;LINUX;LDAP    0
期刊
摘要:介绍了基于Vega平台的虚拟现实系统;GL Studio平台的工作流程;针对GL Studio制作的仪表仿真模型不能在Vega场景中直接应用的问题,分析了Vega和GL Studio的底层软件开发原理;对GL Studio模型向Vega场景移植的几项关键技术进行了研究,解决了坐标系差异、坐标系基本单位转换、模型载入以及场景中的模型操作等问题。  关键词:Vega;GL Studio;视景仿真
期刊
摘要:采用基于增量复制来解决分布式系统的数据同步是一个简单易实现的方法。文章利用Oracle的高级复制技术实现了分布式数据库系统间的数据同步,并给出了PL/SQL命令方式的实现过程。  关键词:复制;分布式;数据库;同步;应用    0引言    基于WAN的分布式管理信息系统是当前跨多地域企事业单位信息处理的首选。福建省运政管理信息系统是覆盖全省14个市运管处、84个县运管所的WAN分布式网络管
期刊
摘要:密写(Steganography)是信息隐藏(Information Hiding)的一个重要分支。鉴于密写比信息加密更安全以及网络与信息安全问题变得越来越重要,密写已经成为当前国际上的研究热点。JPEG是一种常见的图像格式,在JPEG图像中进行密写有着重要的实际意义。文章主要研究了以JPEG图像为载体进行数据密写的基本模型,以及目前国内外以JPEG图像为载体的密写算法。  关键词:信息隐藏
期刊
摘要:数据分页是基于数据库的动态Web系统所必需的技术。Microsoft公司的ADO(Active Data objecc)数据存取技术提供了一种基于Recordset的数据分页方法,该方法简单易用,却由于效率原因,不适用大数据量的快速定位分页。文章对该方法从根本上进行了改进,获得了非常明显的效果,完全适用于企业级应用的海量数据分页定位。  关键词:Web分页技术;ADO;Recordset;改
期刊
摘要:首先介绍了RDF查询语言需要满足的一般特性需求,即表达能力、模式感知、程序操纵、合成性和语义功能。然后介绍了七种具有代表性的RDF查询语言(RQL、RDQL、TRIPLE、N3、Versa、SeRQL、SPARQL),以及国外对RDF查询语言在支持图形匹配、关系操作、聚合和分组功能、递归、具体化、容器、命名空间、语言标签、字符和数据类型、继承等方面的比较研究。  关键词:RDF;语义网;查询
期刊
摘 要:把EP9312与其它嵌入式处理器进行了比较,阐述了采用EP9312来构造嵌入式系统的理由。根据EP9312的特点和系统需·求,提出了一种基于EP9312处理器的嵌入式系统的硬件设计方案。介绍了系统的技术指标及其所使用的Super I/O、PHY,接口电平转换芯片、FLASH、SDRAM、bootloader等各组成部分。叙述了提高系统稳定性的一些原则。该系统主要应用于高端打印机、计算机终
期刊
摘要:利用Intel Mobile PlatForm SDK中关于电源管理的开发接口,提出了电源管理的技术方案,解决了教师上课过程中电脑黑屏和屏幕保护程序自动运行的问题,并为相关工程技术人员给出了一个实例作参考。  关键词:黑屏;屏幕保护;电源管理;开发接口    0引言    在多媒体为主的课堂演示教学过程中,经常会碰到这样的问题,老师正在专心讲课,突然屏幕出现黑屏或者进入屏幕保护状态,导致上课
期刊