注册 登录
编程论坛 C++教室

[求助]求助关于文件流的一道题

hzdz 发布于 2007-09-16 23:04, 2061 次点击
编写一个函数,其唯一的形参和返回值都是istream&类型。该函数应一直读取流到达文件结束符为止,还应将读到内容输出到标准输出中。最后,重设流使其有效,并返回该流。

这是c++ primer上的一道题,我以前对文件操作都是用C语言的方法进行,对这个流基本上没个概念,甚至都读不懂题

请各路高手帮我写个程序框架,或者分析也行,叫我干什么,函数是写成下面这样吗?
istream& process_input(istream& is);

我就有点奇怪既然形参是引用了,还返回它干什么,似乎没有必要

请大家不吝赐教,谢谢
24 回复
#2
aipb20072007-09-16 23:43

[QUOTE]istream& process_input(istream& is);
我就有点奇怪既然形参是引用了,还返回它干什么,似乎没有必要[/QUOTE]

cin >> a >> b

可以连用不是吗?


#3
HJin2007-09-17 00:45
回复:(hzdz)[求助]求助关于文件流的一道题

this is an easy task. I choose to give an answer instead of explain the "why"s --- since there is no why can be asked here.

=================================================== ===============================

#include <iostream>
#include <fstream>
using namespace std;

/**
编写一个函数,其唯一的形参和返回值都是istream&类型。该函数应一直读取流到达文件结束符为止,还应将读到内容输出到标准输出中。最后,重设流使其有效,并返回该流。

这是c++ primer上的一道题,我以前对文件操作都是用C语言的方法进行,对这个流基本上没个概念,甚至都读不懂题

请各路高手帮我写个程序框架,或者分析也行,叫我干什么,函数是写成下面这样吗?
istream& process_input(istream& is);

我就有点奇怪既然形参是引用了,还返回它干什么,似乎没有必要

*/
istream& process_input(istream& is)
{
char ch;
while(!is.eof())
{
is>>noskipws>>ch;
cout<<ch;
}

is.clear();

return is;
}


int main()
{
ifstream ifs("a.txt");
process_input(ifs);
ifs.close();

return 0;
}

#4
hzdz2007-09-17 11:32

感谢二楼三楼的回复,我也是像三楼这么写的。。用vector<string>保存,不过打不出来。。,

看来我的理解还是有点靠谱的,谢谢你们
下面是我自己的函数,大家帮我看看哪出错了,打不出来

istream &process_input(istream& is)
{
string str;
vector<string> svec;

while (is>>str,!is.eof())
{
cout<<"正在进行读入操作,请稍候..."<<endl;
if (is.bad())
{
throw runtime_error("该文件已经损坏,不可读取!");
}
if (is.fail())
{
cerr<<"有一个错,重试"<<ends;
is.clear();
continue;
}

svec.push_back(str);
}

cout<<"已经读入文件,现在将其打印出来"<<endl;
for (std::vector<string>::const_iterator it=svec.begin();it!=svec.end();++it)
{
cout<<*it<<" ";
}
cout<<endl;

return is;
}

我自己测试的时候使用ifstream绑定一个txt文件,打不出来。。

#5
hzdz2007-09-17 11:34
再回复三楼版主:

C++正处于学习当中,好些东西以前没接触过,像cout<<xx<<yy<<..这种只是机械地会使,而不知其所以然。所以问的问题有旱可能会比较菜,还望不要见怪哈。
#6
HJin2007-09-17 13:10

live to learn, brother.

#7
hzdz2007-09-17 14:47
6楼版主,看看我4楼贴出来的,我认为和你的一样,但用一个fstream测试时,一个都打不出来
#8
HJin2007-09-17 15:35
回复:(hzdz)6楼版主,看看我4楼贴出来的,我认为和...

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;

/** output (first three lines are the contents of a.txt)
/O1 minimize space /Op[-] improve floating-pt consistency
/O2 maximize speed /Os favor code space

2nd version
14
已经读入文件,现在将其打印出来
/O1 minimize space /Op[-] improve floating-pt consistency /O2 maximize speed /Os
favor code space
Press any key to continue . . .

*/
istream& process_input(istream& is)
{
char ch;
while(!is.eof())
{
is>>noskipws>>ch;
cout<<ch;
}

is.clear();

return is;
}

istream &process_input2(istream& is)
{
string str;
vector<string> svec;

// 1. you will have trouble for the '\n' character.
// 2. you may use getline to read a line insteand of a string each time
while (!is.eof(), is>>str)
{
//cout<<"正在进行读入操作,请稍候..."<<endl;
//if (is.bad())
//{
// throw runtime_error("该文件已经损坏,不可读取!");
//}
//if (is.fail())
//{
// cerr<<"有一个错,重试"<<ends;
// is.clear();
// continue;
//}

//is>>str;
svec.push_back(str);
}

cout<<svec.size()<<endl;

cout<<"已经读入文件,现在将其打印出来"<<endl;
for (std::vector<string>::const_iterator it=svec.begin();it!=svec.end();++it)
{
cout<<*it<<" ";
}
cout<<endl;

return is;
}

int main()
{
ifstream ifs("a.txt");
process_input(ifs);
ifs.close();

std::skipws(ifs);
cout<<"2nd version "<<endl;
ifs.open("a.txt");
process_input2(ifs);
ifs.close();

return 0;
}

#9
hzdz2007-09-17 16:19

首先谢谢你

这里关于逗号表达式的应用有一点疑问:
我原来先判断是不是有输入,再判断输入是不是结尾。这样的话不管读入过程中发生什么问题,都可以继续读入,直到遇到文件结束符。而你改进以后的函数只管读入,万一有错(无论出现bad还是fail),流都处于错误状态,文件读入就不会完全

不知道我的理解对不对?请指教

#10
hzdz2007-09-17 16:37
版主,刚才把你的东西复制进去。。调试不对啊,我的环境是vc6
#11
hzdz2007-09-17 16:38

注掉第二个函数,只用第一个,无限读入

注掉第一个函数,只用第二个,vector.size()==0

#12
HJin2007-09-17 19:13
100% working on my machine.

VC 8.0

#13
hzdz2007-09-17 20:11
我的是vc6.0。。我再试试吧
#14
hzdz2007-09-17 21:06

问题全部解决,我全程犯了个错误,鉴于这个错误是如此低级。。。就不说了

hJin版,你的函数正确,我的也能用~

Thank you so much

#15
hzdz2007-09-17 22:08
新问题:为什么我的测试文件中最后一个字符后面必须得加个空格呢?
#16
HJin2007-09-17 22:19
cout<<*iterator<<" ";

will add a blank (probably superfluous).
#17
hzdz2007-09-19 11:43

谢谢HJIN版,我现在基本明白eof()判断什么了

还有一个问题,我在开贴的时候提出的:这种函数为什么要写成stream& func(stream$)的形式,二楼aipb2007版给我解释的是因为便于连用我有些不太理解。。

按我们一般理解函数的方式,引用形参传递是非复制传递,我们可以在函数中直接处理传递进来的流,而不是复制这个流(流本身的特性也决定它不能复制,这点恰好成立)。只是这里:函数得到实参的引用然后又返回实参的引用的意义怎么来体现呢?是为了出现可能的比如stream& sample=func(stream&)的用法吗?

还是像aipb2007版所说的那种cin>>a>>b式的连用?

如果是后者,那么前面那个process_input函数,您可以举个例子给我看一下这种用法吗?

#18
HJin2007-09-19 12:55
回复:(hzdz)谢谢HJIN版,我现在基本明白eof()判断什...

yes, it is the chain action which demands the function prototype. This is very similar to


// assignment:
a = b = 2;

or insertion and extraction:

cout<<a<<b<<endl;

cin>>a>>b;





Consider the following code for overloading >> for a class:



class A
{
friend std::istream& operator>> (std::istream& is, A& obj);
public:
    A(int i_=0) : i(i_) { }
private:
    int a;
};

std::istream& operator>> (std::istream& is, A& obj)
{
    int i;
    is>>i;
    obj.a=i;

    return is;
}



in main you can do this:


    A objA;
    double d;

    cin>>objA>>d;

#19
hzdz2007-09-19 15:52
回复:(HJin)回复:(hzdz)谢谢HJIN版,我现在基本...

嗯,这个现在也基本明白,谢谢HJin版,不过又有新问题
关于ofstream::app,请看我下面的代码
我就是实现了一个简单的txt文件copy,通过main形参指定读和写的文件名(无须扩展名)
打开文件的模式在我用红色标明那行里指定,按理说如果我多次对同一文件做写入操作,那个文件里应该有多个读入文件的副本,可事实是:显式指定app和未指定是一样的,就好像显式地指定了out
我的环境是vc60,请你在vc80下试试

还有一个问题:我在这个程序里使用vector保存,方法是申请了全局的vector,如果我想在main里申请这个vector,怎么把它传到process_input里让它能动态增长为好?用svec_it& svec?这种引用方式?或者如果是你,你会怎么处理,请传授点经验~谢谢!
#include <iostream>
#include <vector>
#include <string>
#include <fstream>

using namespace std;

typedef std::vector<string>::const_iterator svec_it;
vector<string> svec;

ofstream& output_file(ofstream& stm,string& filename,svec_it beg,svec_it end)
{
stm.close();//奇怪,第一次一定得先关掉清理一次流,再重新打开才行。
stm.clear();

stm.open(filename.c_str(),ofstream::app);//这里奇怪
if (!stm)
{

cout<<"文件打开失败"<<endl;
stm.setstate(ofstream::badbit);
return stm;
}

for (svec_it iter=beg;iter!=end;++iter)
{
stm<<*iter<<endl;
}
stm<<flush;

cout<<"文件存储完成"<<endl;

stm.close();
stm.clear();
return stm;

}
istream &process_input(istream& is)//本函数作用:读入流,打印出来
{
if (!is)
{
cout<<"打开指定文件失败"<<endl;
is.setstate(istream::badbit);

// is.clear();
// is.ignore();
return is;
}
string str;

int flag=1;

cout<<"正在进行读入操作,请稍候..."<<endl;
while (getline(is,str),!is.eof())
{

if (is.bad())
{
throw runtime_error("该文件已经损坏,不可读取!");

cout<<"系统IO错误,无法读取"<<endl;
}
if (is.fail())
{
cerr<<"第"<<flag<<"处读取错误,跳过"<<endl;
is.clear();
is.ignore();
++flag;
continue;
}

svec.push_back(str);
++flag;

}

/* cout<<"已经读入文件,现在打出来"<<endl;
for (svec_it it=svec.begin();it!=svec.end();++it)
{
cout<<*it<<endl;
}
cout<<endl;
*/
return is;
}


int main(int argc,char** argv)
{
while (argc!=3)
{
cout<<"参数数目不符"<<endl;
cout<<"按q退出"<<endl;

char a;
cin>>a;
if ('q'==a)
{
return EXIT_FAILURE;
}
else
{
continue;
}
}
//前面写完了参数错误时候的情况,下面写正确处理
string str_input(".txt");
str_input=argv[1]+str_input;
ifstream input(str_input.c_str());
process_input(input);
if (input.bad())
{
cout<<"调用文件处理函数读入操作失败,请检查输入文件名"<<endl;
input.clear();
input.ignore();
return EXIT_FAILURE;
}
else
{
cout<<"已经处理完读入文件,现在将文件写入预定的文件中...;"<<endl;
}

string out_file(".txt");
out_file=argv[2]+out_file;

ofstream output(out_file.c_str());
//output.close();这个文件输出流有点怪,如果这里使用时候不进行关掉清理流的操作,在调用函数里就得补上这一步
//output.clear();

output_file(output,out_file,svec.begin(),svec.end());
if (output.bad())
{

cout<<"调用文件处理函数写入操作失败,请检查该文件是否设置了只读或其它设备原因"<<endl;
output.close();
output.clear();
return EXIT_FAILURE;
}
else
{
return EXIT_SUCCESS;

}



}

#20
hzdz2007-09-20 17:32
无人过问,自顶一贴
#21
aipb20072007-09-20 22:12
如果是我的话,我会把ofstream作为函数的局部变量。这样安全,执行完自动死掉。

可以把vector作引用传递。
#22
hzdz2007-09-20 22:16

嗯,这两点明白了,现在就剩最后一个问题: ofstream::app的作用在我的程序里似乎没体现出来,每次打开文件都是以out模式打开的,这是为什么?

#23
aipb20072007-09-20 22:30
看不出什么问题!
#24
hzdz2007-09-21 15:55
看起来是没问题。。可是程序运行就不对,第二次打开同一文件的时候,不是在文件尾部插入内容,而是直接覆盖原来的东西
#25
qq5521585092011-09-07 12:39
楼主解决没有啊??我也遇到了相同的问题、、望楼主讲解啊……………………………………
1