我一开始对文件的操作也是很迷惘,于是就找来《 c++ primer 》仔细研究了一下。并作了一个学习笔记。希望和我一样对文件操作比较困惑的网友能够对文件流有一点点的更深的认识。
声明一点:我这个笔记只是针对文件流的操作,如果你发现了我程序中的别的逻辑错误,请别惊讶!!!
下面正式开始。
首先要明白的就是:fstreamàiostream;;ofstreamàostream;;ifstreamàistream…..(继承关系)。
一个例子:
fstream file(“e://test.txt”,ios_base::out);//第一个参数为文件的绝对(相对)路径。第二个参数为打开模式。
打开文件后的定位操作。(fstream::seekg(),注意是文件流对象的成员函数。)
seekg()/seekp()两个重载成员函数如下:(---g()表示读定位,---p()表示写定位)。
seekg(pos_type current_position);//一个绝对位置(指到“有效”字符,空格不算),0—表示文件开头。(字节为单位)
seekg(off_type offset_position,ios_base::seekdir dir);//第一个参数为相对(是指相对于第二个参数指示的定位指引)偏移量;第二个参数就是定位指引。可取如下值(3种):
1/3 ios_base::beg
2/3 ios_base::cur
3/3 ios_base::end
fstream的当前位置由谁来返回?(注意一下类型,是ios_base::pos_type)。
由tellg()/tellp()来返回。(不用参数。)返回类型为:ios_base::pos_type.
给一个例子:
ios_base::pos_type mark=ifile.tellp();
给出一个实例的分析过程:(要好好理解:))
//例程的功能是:从文件中读取所有行文字,统计每行文字个数和最终文字个数,并写到文件中。
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
fstream iofile;
iofile(“e://text.txt”,ios_base::in|ios_base::out|ios_base::app)//与一个文件关联。
If(!iofile)
{
cout<<”don’t open the file!”<<endl;
return –1;
}
int nCounter;
char ch;
while(iofile.get(ch))
{
cout.put(ch);//STDOP显示。
++nCounter;
if(ch==’/n’)
{
iofile<<nCouter;
iofile.put(‘’);
}
}
iofile<<nCounter<<endl;
out<<”[”<<nCounter<<”]”<<endl;
return 0;
}
对于一段文字:
Please call me kuaidh00,
I am work at ZhongNanHai Ltd.
程序的执行结果为:[0].
显然不是我要的结果呀,分析如下:
第一次分析过程。如下:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
fstream iofile;
iofile(“e://text.txt”, ios_base::in|ios_base::out|ios_base::app);//以ios_base::app方式打开,一开始就定位到
//了文件的尾部,当然读不到内容了。实际使用时只用ios_base::app总是打开文件失败的,
//个人认为可能《c++ primer》中作者没有测试程序,所以这里可能有错误。
//我加上ios_base::out就OK了。
iofile.seekg(0);//设置定位到文件开头了。
If(!iofile)
{
cout<<”don’t open the file!”<<endl;
return –1;
}
int nCounter;
char ch;
while(iofile.get(ch))
{
cout.put(ch);//STDOP显示。
++nCounter;
if(ch==’/n’)
{
iofile<<nCouter;
iofile.put(‘’);
}
}
iofile<<nCounter<<endl;
out<<”[”<<nCounter<<”]”<<endl;
return 0;
}
输出:
Please call me kuaidh00,
[21]
第一次修改后,只正确地统计了第一行的文字,后面的行都被忽视了。再次分析。
第二次分析如下:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
fstream iofile;
iofile(“e://text.txt”, ios_base::in|ios_base::out|ios_base::app);//以ios_base::app方式打开,一开始就定位
//到了文件的尾部,当然读不到内容了。
iofile.seekg(0);//设置定位到文件开头了。
If(!iofile)
{
cout<<”don’t open the file!”<<endl;
return –1;
}
int nCounter;
char ch;
while(iofile.get(ch))
{
cout.put(ch);//STDOP显示。
++nCounter;
if(ch==’/n’)
{
iso_base::pos_type mark=iofile.tellg();//写东东前,获取并存储当前文件定位处。
iofile<<nCouter;//分析此处文件的当前位置时发现,其文件指针位置是在文件的结尾了
//因为,是在文件尾app字符的,所以定位指针当然是在文件的尾部了。
//此时,在while()条件中是读到文件结束。所以结束循环。所以只读了
//第一行。解决方法就是:当写入完毕东东之后,就重新回到写入前的文件定位处。
iofile.put(‘’);
iofile.seekg(mark);//写入完毕后,要设置回原来的文件定位处,继续统计才对。
}
}
iofile<<nCounter<<endl;
out<<”[”<<nCounter<<”]”<<endl;
return 0;
}
STDOP输出:[53]
哈哈。。经过两次的坚苦努力,终于正确出字数了。
但是,但是,当我打开文件时却发现这个“53”并没有写到文件中!!哎,高兴的太早了。再分析如下:
........>>>请参看“关于文件流的阅读笔记--续篇”。
<<<<........上接第二次分析。
第三次分析:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
fstream iofile;
iofile(“e://text.txt”,ios_base::in|ios_base::app);//以ios_base::app方式打开,一开始就定位
//到了文件的尾部,当然读不到内容了。
iofile.seekg(0);//设置定位到文件开头了。
If(!iofile)
{
cout<<”don’t open the file!”<<endl;
return –1;
}
int nCounter;
char ch;
while(iofile.get(ch))//读到“文件结束符”就会退出循环。
{
cout.put(ch);//STDOP显示。
++nCounter;
if(ch==’/n’)
{
iso_base::pos_type mark=iofile.tellg();//写东东前,获取并存储当前文件定位处。
iofile<<nCouter;//分析此处文件的当前位置时发现,其位置是在文件的结尾了
//因为,是在文件尾app字符的,所以定位当然是在文件的尾部了。
//此时,在while()条件中是读到文件结束。所以结束循环。所以只读了
//第一行。解决方法就是:当写入东东之后,就重新回到写入前的文件定位处。
iofile.put(‘’);
iofile.seekg(mark);//写入完毕后,要设置回原来的文件定位处,继续统计才对。
}
}
iofile.clear();//清除状态标记,你还记得吗?
//iofile.clear()其实就是iofile.clear(ios_base::goodbit)。
iofile<<nCounter<<endl;//为什么没输出到文件中?原来又是“文件结束符状态”作的怪。
//每当文件流遇到“文件结束状态”时,文件是认此文件不可读又不可写了。
//如果要再想往文件输出,那就要在文件流中清除“结束标记”这种状态。
out<<”[”<<nCounter<<”]”<<endl;
return 0;
}
经过三次修正终于完成了正确的程序了。现在可以大声笑了。哈哈哈。。。。。。
流的条件状态:(非常重要)
每个流都维护一组状态:(4个)
1/4 .eof() (end of file,)遇到文件结束符时,返回true.
If(iofile.eof())
//可以都读进来。
2/4 .bad() (seekg/seekp超出了文件范围时,返回true.)
3/4 .fail() (打开不成功,或者输入格式错误时,返回true)
4/4 .good() (其它都不为true,它返回true)
可以两种方法设置条件状态:
ios_base::eofbit//状态属性。
ios_base::badbit
ios_base::failbit
ios_base::googbit
第一种方法:.clear()。
为什么说.clear()是显式的???因为如下:(清除所有状态后,再设置新的状态(可为多个))。
iofile.clear(ios_base::goodbit);
iofile.clear();两个是一样的,默认值为ios_base::goodbit.
第二种方法:.setstate();
这个流对象成员函数可增加一种状态。如:
iofile.setstate(ios_base::failbit|ios_base::eofbit);
读取流的当前状态:rdstate()。返回类型为:ios_base::iostate
再提一下:#include <sstream>,stringstream.字符串-流。
同样:ostringstream/istringstream。还有stringstream。
一个经典cin>>(int/float)死循环的解决方案:
要分两个步骤:
(1)、、cin.clear();//清空cin流的状态,并设置成ios_base::goodbit状态。
(2)、、fflush(stdio);//在<stdio.h>中,作用:清空输入缓冲区。
通过上面两步骤就可以完美解决这个死循环问题。
--------------- ---------------------文件流知识-补充完毕--BY kuaidh0