getline成员函数分析

来源:互联网 发布:淘宝店女装退货率30 编辑:程序博客网 时间:2024/04/30 08:58

今天一个学生写了如下代码段,其目的将一个文本文件的内容输出到屏幕上。

ifstream in("file3.txt")char buf[3];while(!in.eof())  {       in.getline(buf,  sizeof(buf));cout<<buf<<endl;}

file3.txt的内容如下:

abcefg

在执行的时候程序输出一个ab之后便进入了死循环。原因何在?

经过一个小时的查资料和看代码,终于大致搞清问题所在,与getline的实现机制有关。

getline成员函数的声明如下

basic_istream<Elem, Tr>&

getline(     char_type *_Str,      streamsize _Count,       char_type _Delim);
第一个参数是字符缓冲区地址,第二个是缓冲区长度,第三个是分隔符(默认是回车)。

其实现的大致流程是:

1、首先判断istream的failbit位是否为1,为1的话意味着输入流的状态有错误,则不进行读操作,getline函数结束执行

2、从当前位置开始从输入流中依次读取单个字符并拷贝到缓冲区,直到遇到下列条件满足时,循环结束。

(1)遇到文件尾时停止读操作,并设置流对象的结束标记为1

(2)读到调用者指定的分隔符时,此时将分隔符之前的字符拷贝到缓冲区中,但分隔符本身不拷贝进去,并且下次读操作将从分隔符后的下一个字符开始。

(3)已经读了n-1个字符(n是调用者传入的第二个实参_Count的初值),此时要把流对象的错误标志位置1(为什么要这么干,我也不知道,个人觉得这么设计不太合理....)

当循环结束后,gelline函数会在字符串的尾部加一个C风格的结束符'\0'。


这样,学生遇到的现象就可以解释了。

首先,由于file3.txt文件是存在的,所以in对象开始时的状态是正常的,因此第一次getline将会执行,由于缓冲区的长度是3,因此在读完ab两个字符之后getline内部的循环便终止了。此时getline会把in对象的failbit设为1,但文件还未读到尾部,所以in.eof()为false,这样在第二次进入while循环体时,循环条件!in.eof()为true,于是继续执行getline函数

,但是由于第一次的getline操作已经把in对象的failbit设为1,第二次的getline便不进行任何读操作了,此时流的指针和流的状态均未发生变化,于是第三次循环时与第二次循环一样,循环条件为真,可以进入循环体,in对象的failbit设为1,getline函数不进行读取操作,如是反复,便导致了死循环。

知道了病因,于是便有了下面的解决方案

while(!in.eof()){in.getline(buf,sizeof(buf));//将if(in.fail() && in.gcount()==(sizeof(buf)-1))             in.clear();       cout<<buf<<endl;}
这里的clear成员函数的作用是将in对象的状态设回正常状态。gcount函数返回上次读操作中从输入流中提取的字符数(包括分隔符)。

请读者自行分析和改进这个解决方案。




原创粉丝点击