小心为上:注意C++ fstream给你设下的陷阱

 

                      小心为上:注意C++ fstream给你设下的陷阱  

       

透过名字看本质:到底什么是stream

 stream的定义

stream的中文翻译为“流”,不是很好理解,我们来看英文关于stream的定义,比较常见的有两个:

1.      A stream is an abstraction that represents a device on which input and output operations are performed.

2.      A stream is a "stream of data" in which character sequences "flow."

 

英文看起来比较累,总结一下,提炼如下几个关键点:

1.      abstraction that represents a device:代表一个设备;

2.      stream of data:数据流,隐含了FIFO的一个特性;

3.      character sequences flow:字符序列在其中流动,而不是二进制在其中流动;

 

多说无益,还是看图说话:

 

 

注:上图中暗含一个玄机:stream的数据是设备数据的一个子集,因为stream只是代表一个设备,而并不是完全等于一个设备。

 

关公战秦琼: stream vs buffer

一个容易和stream混淆的概念就是大家常见的buffer,buffer的定义为(参见WIKI Data Buffer):A buffer is a region of memory used to temporarily hold data while it is being moved from one place to another.

英文看起来也比较累,还是总结一下:

1.      region of memory:一块内存区域,因此就隐含了随机访问和二进制的操作方式;

2.      one place to another:数据移动过程中使用

 

经过对两个术语定义的分析总结,相信大家都明白了这两者之间的差异了,其实这就是一个关公战秦琼的例子,因为它们两个实际上并不是一个竞争关系。

汇总对比点如下:


对比点


stream


buffer


作用


代表一个设备


数据临时存储


访问方式


FIFO


随机访问


数据内容


字符流


二进制

当然,并不是说stream和buffer就毫无关系了,stream为了提高性能,实现的时候就用到了buffer。

小心为上:注意fstream设下的陷阱

2.1  char和wchar_t的操作

当你使用<<输出的时候,任何字符都可以输出;但当使用>>进行输入的时候,开头的空白字符缺省情况下是跳过的。

 

例如:假设文件中有这么一个字符串“       test”,则使用>>读入的时候,直接就读到了字母‘t’,而不会是空格。

避免这个陷阱的招数:

1.      清除skipws标志:unsetf(ios::skipws)

2.      使用get()函数

2.2  指针类型的操作

1.      char*

char*类型的数据输出时会将字符串全部输出,但输入的时候你千万要注意,输入的时候默认会将前面的空白字符全部去掉;而且输入时默认是以空格来作为分隔符的,例如:“This is a test C-string.”可以全部输出,但如果文件中有这么一串字符串,那么用<<是无法全部读入的,只能读入“This”“is”“a”“test”“C-string”.

避免这个陷阱的招数:

1.      如果想改变默认去掉头部空白字符的操作方式,请参考skipws

2.      如果想改变以空格作为结束符的操作方式,对不起,用<<是没有办法的,只能用istream& istream::get (char* str, streamsize count, char delim)或者

istream& istream::getline (char* str, streamsize count, char delim)

 

2.      void*

直接输出指针地址。

 

3.      其它指针

不管是指向int/double等标准数据类型,还是自定义的struct/class类型,都是输出指针指向的地址,而不是输出指针指向的对象。

2.3  eof和fail

2.3.1  eof

eof从字面意思来看,当然是end of file,用于表明当前已经到了文件末尾,不能再读了。

但这里有一个很迷惑的陷阱:只要遇到结束符,流就会将状态置为EOF,而不管置位前的操作是否成功。

例如,使用getline函数读取文件的最后一行,如果这一行是因为遇到了EOF而结束的,那么getline操作是成功的,但eof还是会置位。

 

因此,不能在调用函数后通过eof来判断函数调用是否读到文件末尾了,而应该直接判断调用本身是否成功,具体样例请看fail。

2.3.2  fail

导致fail标志位置位的有如下常见的情况:

1.      文件不存在;

2.      文件不能创建;

3.      eof标志位置位;

4.      非法的格式,例如当你期望数字的时候,而文件里面却是字母

 

注意第三种情况,在文件eof的时候也会同时置fail,所以,循环读取文件的时候,要将fail和eof结合起来使用:在循环判断中使用fail,fail失败后再使用eof。

错误的用法:

1:    std::ifstream file("test.txt");

2:    std::string word;

3:    double value;

4:    while ( true ) {

5:      // A word and a double value were both read successfully

6         file >> word >> value;

7         if( file.eof() )

            break;

9:    }

 

正确的用法

1:    std::ifstream file("test.txt");

2:    std::string word;

3:    double value;

4:    while (file >> word >> value) {

5:      // A word and a double value were both read successfully

6:    }

7:    if (!file.eof()) throw std::runtime_error("Invalid data from file");

 

2.4  fstream的binary打开方式

文件流的打开方式中有一个binary,从字面意思来看,应该是按照二进制打开文件,然后进行二进制读写。

然而这样理解的话,就陷入了C++的陷阱:binary实际上和二进制读写没有关系,binary只是为了告诉系统是否将不同操作系统间特定的字符替换,最典型的是换行符,在windows上是/r/n,而在Unix类系统上是/n,如果加了这个binary标志,流就不会自动替换。

 

那C++的fstream流如何进行二进制读写呢?其实很简单:只需要调用get/read和put/write即可。也就是说:是否是二进制读写和文件打开方式无关,而是和调用函数有关。如果使用<<和>>,则就是按照字符流进行读写;如果使用get/read和put/write,则按照二进制读写。

 

前面说过流是character sequences flow,那为什么这里又说fstream能够按照二进制读写呢?由于没有研究过相关的实现代码,因此这里无法给出分析,个人推断应该是流的内部实现将字符流转换为二进制了。

 

时间: 2024-11-05 18:58:31

小心为上:注意C++ fstream给你设下的陷阱的相关文章

属性表标签上的位图在MFC、CB下的实现

属性表标签(tab control)支持在每一个item上放Image 图片.在mfc下实现相当简单,可分为以下几步: step1: create a bitmap resource with the images you can also use icons or even create the images at run time. the size of the images should be in proportion to the height of the label. step2

win7电脑如何设置插上耳机就能播放声音,拔下耳机就自动禁音

  我们知道,在所有的电脑中都是这样的,咱们插上耳机,电脑就会自动的切换到耳机模式,但是如果拔下耳机,咱们电脑的声音没有关闭掉的话,就会直接的外放出来,这样就很容易影响到身边的人.特别是对于一些在上班时候使用的用户,就是这样的.那么有什么办法可以解决呢?下面,小编就来介绍一个方法,在w764位旗舰版下载中,咱们插上耳机就能播放声音,拔下耳机就自动禁音,如果你也想知道这个操作方法的话,下面就和小编一起来看看吧! 1.首先,咱们单击打开win7旗舰版电脑的开始菜单,然后从其中点击选择控制面板选项.

dedecms修改“上一篇:没有了”和“下一篇:没有了”的显示内容

对于在使用dedecms的过程中,我们需要有一些个性化的需求,比如修改"上一篇:没有了"和"下一篇:没有了"的显示内容,我想改成"Previous: No"及"Next: No",或是改成任何我们希望能实现的样式及文字,哪么我们应该如何实现呢? 这个时候,我们需要通过修改include/arc.archives.class.php文件,来实现我们个性化的功能需求,打开文件. 查找 大概在813行: $this->PreN

mysql数据库-远程连接服务器上,在自己的帐号下建立java连接mysql的程序,但是一直连接不上

问题描述 远程连接服务器上,在自己的帐号下建立java连接mysql的程序,但是一直连接不上 我是通过远程连接在实验室机房的一台机器(linux操作系统)上,然后在该机器上建立JAVA程序,其中涉及到连接mysql数据库的操作,但是在获取连接时一直提示"Communications link failure",涉及的代码为:connect=DriverManager.getConnection(""jdbc:mysql://localhost:3306/pyq_te

uitableview-UITableView的上如何顶部嵌套一个UIScrollview实现下拉放大的效果

问题描述 UITableView的上如何顶部嵌套一个UIScrollview实现下拉放大的效果 就是在UITableView的顶部放一个UIScrollView.UIScrollView上有三页,每页是一个UIImageView,每次下拉的时候图片能放大, 解决方案 大神能写个Demo不 解决方案二: 不用这么麻烦,UITableView本身就继承自UIScrollview,tableview的第一个cell放scrollview,在下拉的时候,改变tableview的第一个cell的高度就行.

easyui-easyUI中如何将上一页的数据传递到下一页啊

问题描述 easyUI中如何将上一页的数据传递到下一页啊 第一页是一个表格,表格的每一项都有对应操作,当点击操作后跳转到下一页,并且下一页要显示这个表格这一行的数据,怎么写啊.求大神啊o(╯□╰)o 解决方案 <table><tr id="tr"><td>1</td><td>2</td></tr></table> <script> function addParam(a) {//

xml-关于安卓设置按钮回退到上一个activity的问题 ,问下大神两种方法的不同。

问题描述 关于安卓设置按钮回退到上一个activity的问题 ,问下大神两种方法的不同. 1.第一种是常规的添加按钮监听,使用finish回退到上一个activit运行成功. 2.查资料后,还有一种解决方式为在xml文件按钮中设置android:onClick=""back"",然后在调用当前xml文件的activity中编写back()方法包含finish()结束当前的activity,自己编写的代码不报错,但是回退到上一个界面是会弹出无法运行界面,然后回退到上一

站在风口上,做猪都可以,但下一个风口在哪呢?

摘要: 站在风口上,做猪都可以,但下一个风口在哪呢?比尔盖茨给出的答案是:计算机视觉(computer vision)与深度学习(deep learning) 的结合.2014年 6 月中旬他来北京,去的第一家创业公司 站在风口上,做猪都可以,但下一个风口在哪呢?比尔·盖茨给出的答案是:计算机视觉(computer vision)与深度学习(deep learning) 的结合.2014年 6 月中旬他来北京,去的第一家创业公司就是做计算机视觉的. 这家公司叫格灵深瞳.今年 6 月刚刚获得来自红

asp.net 如何才能把图片上传到其他盘呢?不要上传在当前程序的目录下!

问题描述 asp.net如何才能把图片上传到其他盘呢?不要上传在当前程序的目录下!使用最好控件FileUploadImage控件请给个例子谢了 解决方案 解决方案二:没人知道吗?高手帮忙实现下!在线等...解决方案三:先用netuse登陆局域网的机器保存的时候路径直接写\192.168.1.8D$Images解决方案四:直接写文件夹路径...开可写~