fstream

文件输入和输出
如果 一个用户希望把一个文件连接到程序上,以便用来输入或输出,则必须包含 fstream 头文件(它又包含了iostream头文件):
#include
为了打开一个仅被用于输出的文件,我们可以定义一个ofstream(输出文件流)类对象。例如:
ofstream outfile( "copy.out", ios_base::out );
传递给ofstream构造函数的实参分别指定了要打开的文件名和打开模式。一个ofstream文件可以被打开为输出模式(ios_base::out)或附加模式(ios_base::app)。(在缺省情况下,一个ostream文件以输出模式打开。)outfile2的定义与outfile的定义等价:

// 缺省情况下,以输出模式打开
ofstream outfile2( "copy.out" );

如果在输出模式下打开已经存在的文件,则所有存储在该文件中的数据都将被丢弃。如果我们希望增加而不是替换现有文件中的数据,则我们应该以附加模式打开文件。于是,新写到文件中的数据将被添加到文件尾部。在这两种模式下,如果文件不存在,则程序都会创建一个新文件。
在试图读写文件之前,先判断它是否已被成功打开,这总是一个不错的主意,我们可以按如下方式测试outfile:

if ( ! outFile ) { // 打开失败
cerr << "cannot open "copy.out" for output\n";
exit( -1 );
}

ofstream从ostream类派生。所有ostream操作都可以被应用到一个ofstream类对象上,例如:

char ch = ' ';
outFile.put( '1' ).put( ')' ).put( ch );
outFile << "1 + 1 = " << (1 + 1) << endl;

向outfile中插入:

1) 1 + 1 = 2

以下的程序从标准输入获取字符,并将它输出到copy.out中:

#include

int main()
{
// 打开文件copy.out用于输出
ofstream outFile( "copy.out" );

if ( ! outFile ) {
cerr << "Cannot open "copy.out" for output\n";
return -1 ;
}

char ch;
while ( cin.get( ch ) )
outFile.put( ch );
}

用户定义的输出操作符实例也可以被应用到ofstream类对象上。下面的程序调用了上节定义的WordCount输出操作符:

#include
#include "WordCount.h"

int main()
{
// 打开文件word.out用于输出
ofstream oFile( "word.out" );

// 在这里测试是否打开成功...

// 手工创建和设置WordCount对象
WordCount artist( "Renoir" );
artist.found( 7, 12 ); artist.found( 34, 18 );

// 调用 operator <<(ostream&, const WordCount&);
oFile << artist;
}

为了打开一个仅被用于输入的文件,我们可以使用ifstream类对象。ifstream类从istream类派生。下面的程序读取一个由用户指定的文件,并将内容写入到标准输出上:

#include

int main()
{
cout << "filename: ";
string file_name;

cin >> file_name;

// 打开文件 copy.out用于输入
ifstream inFile( file_name.c_str() );

if ( !inFile ) {
cerr << "unable to open input file: "
<< file_name << " -- bailing out!\n";
return -1;
}

char ch;
while ( inFile.get( ch ))
cout.put( ch );
}

下面的程序读入我们的Alice Emma文本文件、用filter_string()过滤它(Alice Emma文本以及filter_string()的定义见20.2.1节)、排序字符串、去掉重复单词,然后把结果文本写入到一个输出文件中:

#include
#include
#include
#include

template
void filter_string( InputIterator first, InputIterator last,
string filt_elems = string("\",?."))
{
for ( ; first != last; first++ )
{
string::size_type pos = 0;
while ((pos=(*first).find_first_of(filt_elems,pos))
!= string::npos )
(*first).erase( pos, 1 );
}
}

int main()
{
ifstream infile( "alice_emma" );

istream_iterator ifile( infile );
istream_iterator eos;

vector< string > text;
copy( ifile, eos, inserter( text, text.begin() ));

string filt_elems( "\",.?;:" );
filter_string( text.begin(), text.end(), filt_elems );

vector::iterator iter;

sort( text.begin(), text.end() );
iter = unique( text.begin(), text.end() );
text.erase( iter, text.end() );

ofstream outfile( "alice_emma_sort" );

iter = text.begin();
for ( int line_cnt = 1; iter != text.end();
++iter, ++line_cnt )
{
outfile << *iter << " ";
if ( ! ( line_cnt % 8 ))
outfile << '\n';
}
outfile << endl;
}

编译并运行该程序,产生如下输出:

A Alice Daddy Emma Her I Shyly a
alive almost asks at beautiful bird blows but
creature fiery flight flowing hair has he her
him in is it like long looks magical
mean more no red same says she shush
such tell tells the there through time to
untamed wanting when wind

在定义ifstream和ofstream类对象时,我们也可以不指定文件。以后通过成员函数open()显式地把一个文件连接到一个类对象上。例如:

ifstream curFile;
// ...
curFile.open( filename.c_str() );
if ( ! curFile ) // open failed?
// ...

我们可以通过成员函数close()断开一个文件与程序的连接。例如:
curFile.close();
在下面的程序中,用同一个ifstream类对象依次打开和关闭了五个文件:

#include

const int fileCnt = 5;
string fileTabl[ fileCnt ] = {
"Melville","Joyce","Musil","Proust","Kafka"
};

int main()
{
ifstream inFile; // 没有连接任何文件

for ( int ix = 0; ix < fileCnt; ++ix )
{
inFile.open( fileTabl[ix].c_str() );
// ... 判断是否打开成功
// ... 处理文件
inFile.close();
}
}

一个fstream类对象可以打开一个被用于输出或者输入的文件。fstream类从iostream类派生而来。在下面的例子中,使用fstream类对象文件,对word.out先读后写。文件word.out,在本节开始时被创建,它包含一个WordCount对象。

#include
#include "WordCount.h"

int main()
{
WordCount wd;
fstream file;

file.open( "word.out", ios_base::in );
file >> wd;
file.close();

cout << "Read in: " << wd << endl;

// ios_base::out 将丢弃当前的数据
file.open( "word.out", ios_base::app );
file << endl << wd << endl;
file.close();
}

一个fstream类对象也可以打开一个同时被用于输入和输出的文件。例如,下面的定义以输入和输出模式打开word.out:
fstream io( "word.out", ios_base::in|ios_base::app );
按位或(OR)操作符被用来指定一种以上的模式。通过seekg()或seekp()成员函数,我们可以对fstream类对象重新定位(g表示为了获取(getting)字符而定位[用于ifstream类对象],而p表示为放置字符(putting)而定位[用于ofstream类对象])。这些函数移动到文件中的一个“绝对”地址,或者从特定位置移动一个偏移。seekg()和seekp()有以下两种形式:

// 设置到文件中固定的位置上
seekg( pos_type current_position );

// 从当前位置向某个方向进行偏移
seekg( off_type offset_position, ios_base::seekdir dir );

在第一个版本中,当前位置被设置为由current_position指定的某个固定的位置,这里0是文件的开始。例如,如果一个文件由下列字符
abc def ghi jkl
构成,则下列调用
io.seekg( 6 );
把io重新定位到字符位置6,在我们的例子中即字符f。第二种形式使用一个偏移来重新定位文件,该偏移值或者是从当前位置开始计算,或者是到文件开始处的偏移,或者是从文件尾部倒退向后计算;这由第二个实参来指定。dir可以被设置为以下选项之一:
1.ios_base::beg,文件的开始。
2.ios_base::cur,文件的当前位置。
3.ios_base::end,文件的结尾。
在下面的例子中,在每次迭代时,seekg()的每次调用都将文件重新定位在第i个记录项上:

for ( int i = 0; i < recordCnt; ++i )
readFile.seekg( i * sizeof(Record), ios_base::beg );

第一个实参可以被指定为负数。例如,下面语句从当前位置向后移动10个字节:
readFile.seekg( -10, ios_base::cur );
fstream文件中的当前位置由下面两个成员函数之一返回:tellg()或tellp()(‘p’表示putting——用在ofstream上,‘g’表示getting——用在ifstream上)。例如:

// 标记出当前位置
ios_base::pos_type mark = writeFile.tellp();

// ...
if ( cancelEntry )
// 返回到原先标记的位置上
writeFile.seekp( mark );

如果程序员希望从文件的当前位置向前移动一个Record,则可以有以下两种写法:

// 等价的做法:用于重新定位的seek调用
readFile.seekg( readFile.tellg() + sizeof(Record) );

// 这种方法被认为效率更高
readFile.seekg( sizeof(Record), ios_base::cur );

我们来更详细地看一个实际的程序设计示例。下面是问题:给定一个要读取的文本文件。我们将计算文件的字节大小,并将它存储在文件尾部。另外,每次遇到一个换行符,我们都将当前的字节大小(包括换行符)存储在文件末尾。例如,已知文本文件:

abcd
efg
hi
j

程序应该生成下面修改之后的文本文件:

abcd
efg
hi
j
5 9 12 14 24

下面是我们的原始实现:

#include
#include

main()
{
// 以输入和附加模式打开
fstream inOut( "copy.out", ios_base::in|ios_base::app );
int cnt = 0; // byte count
char ch;

while ( inOut.get( ch ) )
{
cout.put( ch ); // 在终端回显
++cnt;
if ( ch == '\n' ) {
inOut << cnt ;
inOut.put( ' ' ); // 空格
}
}

// 输出最终的字节数
inOut << cnt << endl;
cout << "[ " << cnt << " ]" << endl;
return 0;
}

inOut是附在文件copy.out上的fstream类对象,它以输入和附加(append)两种模式打开。以附加模式打开的文件将把数据写到文件尾部。
每次读入一个字符时,包括空白字符但不包括文件结束符,我们把cnt加1并在用户终端上回显这个字符。将输入的字符回显到终端上,目的是我们可以看清程序是否像期望的那样工作。
每次遇到换行符时,我们都把cnt当前值写到inQut中。读到文件结束符则终止循环。我们把cnt的最后值写到inQut和屏幕上。
我们编译程序。它似乎是正确的。我们用的文件含有Moby Dick的前几个句子,这是十九世纪美国小说家Herman Melville写的作品:

Call me Ishmael. Some years ago, never mind
how long precisely, having little or no money
in my purse, and nothing particular to interest
me on shore, I thought I would sail about a little
and see the watery part of the world. It is a
way I have of driving off the spleen, and
regulating the circulation.

程序执行时,产生如下输出:
[ 0 ]
没有任何字符被显示出来,程序认为文本文件是空的。这显然不正确。我们对某些基本的概念理解有误。请别着急,也别沮丧,现在要做的事情就是仔细地想一想。问题在于,文件是以附加(append)模式打开的,所以它一开始就被定位在文件尾。当
inOut.get( ch )
执行时,遇到了文件结束符,while循环终止,因此cnt的值是0。
虽然程序执行的结果很糟糕,但是,一旦我们找到了问题,解决的方案也就很简单了。我们所要做的,就是在开始读之前,把文件重新定位到文件的开始处。语句
inOut.seekg( 0 );
实现的正是这样的效果。重新编译并运行程序。这次产生如下输出:

Call me Ishmael. Some years ago, never mind
[ 45 ]

它只为文本文件的第一行产生了显示和计数结果。余下的六行都被忽略了。唉,谁说程序设计很容易?这是程序员成长过程的一部分,尤其是当我们正在学习新东西的时候。(有时候记录下我们误解或做错的事情非常有用——尤其是以后当我们面对比我们更没有经验的人而失去耐心的时候。)现在我们需要做一个深呼吸,想想我们正在努力做什么事情,以及我们显然做了什么事情。你能发现问题吗?
问题在于,文件以附加(append)模式被打开。第一次写cnt时,文件被重新定位到文件末尾,后面的get()遇到文件结束符,因此结束了while循环。
这次的解决方案是把文件重新定位到写cnt之前的位置上,这可以用下面两条语句来完成:
// 标记出当前位置
ios_base::pos_type mark = inOut.tellg();
inOut << cnt << sp;
inOut.seekg( mark ); // 恢复位置

重新编译并执行程序,终端上的输出结果是正确的。但是,检查输出文件,发现它仍然不对:最终字节数虽然已经被写到终端上,但是没有写到文件中。while循环后面的输出操作符没有被执行。
这次的问题是,inOut是处于“遇到文件结束符”的状态。只要inOut处于这种状态,就不能再执行输入和输出操作。方案是调用clear(),清除文件的这种状态。这可以用以下语句来完成:
inOut.clear(); // zero out state flags
完整的程序如下:

#include
#include

int main()
{
fstream inOut( "copy.out", ios_base::in|ios_base::app );
int cnt=0;
char ch;

inOut.seekg(0);
while ( inOut.get( ch ) )
{
cout.put( ch );
cnt++;
if ( ch == '\n' )
{
// mark current position
ios_base::pos_type mark = inOut.tellg();
inOut << cnt << ' ';
inOut.seekg( mark ); // restore position
}
}
inOut.clear();
inOut << cnt << endl;

cout << "[ " << cnt << " ]\n";
return 0;
}

重新编译并执行程序,终于产生正确的输出。我们在实现程序时的一个错误是,没有为需要支持的行为提供一条显式的语句。每一个后续的方案都是针对每一个出现的问题,而不是先分析整个问题,然后提出完整的方案。虽然我们得到了同样的结果,但是与“开始时就全部认真地考虑问题”相比较,我们付出了更多的劳动和代价.



reinterpret_cast    static_csat:http://www.cnblogs.com/chengxin1982/archive/2010/01/13/1646311.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值