使用get函数替代getline引发无限循环,请求问题原因分析
为什么
istream::get()在循环中会导致无限循环? 咱们来拆解你遇到的问题——单独用get()读单行没问题,但放到循环里就无限跑,核心是两个容易踩的坑叠加在一起了:
首先先把你的代码贴出来方便分析:
int main() { ofstream outfile; outfile.open("Myfile.txt", ios::trunc); outfile <<"aabc"<<endl; outfile <<"Hello Helloo"<<endl; outfile <<"3abc"<<endl; outfile <<"Somee text here "<<endl; outfile.close(); ifstream infile; infile.open("Myfile.txt"); char ch[20]; while(!infile.eof()) { infile.get(ch,20); cout<<ch; } infile.close(); return 0; }
第一个坑:while(!infile.eof())的错误逻辑
很多人刚开始用C++文件流都会踩这个坑:EOF标志只有在你尝试读取超出文件末尾的时候才会被设置,不是读到最后一个字符就触发。也就是说,当你读完最后一行内容后,eof()仍然是false,循环会再执行一次——这时候才会尝试读取超出文件末尾,设置EOF,但此时已经执行了一次无效的读取操作了。
第二个坑:get(char*, int)不会消耗换行符
这是导致无限循环的关键:infile.get(ch,20)的行为是读取字符,直到遇到换行符\n或者达到指定的长度(这里是20),但它不会把这个换行符从输入缓冲区里取出来——换行符会留在缓冲区里。
举个具体的流程:
- 第一次循环:读取第一行的
aabc,缓冲区里剩下\n。 - 第二次循环:
get()一开始就碰到缓冲区里的\n,直接停止读取,没有更新ch的内容(或者说只写入了一个空字符串),然后你输出ch(还是之前的aabc)。 - 更糟的是:这次读取并没有触发EOF,因为根本没碰文件末尾,只是碰到了换行符。而且因为
get()没有读取到任何有效字符,流的failbit会被设置,但!infile.eof()仍然是true,循环会一直重复这个过程——每次都读取到空,输出旧内容,永远到不了文件末尾,无限循环。
怎么解决?
这里给你几个可行的方案:
方案1:每次读取后手动清除换行符
在get()之后,用ignore()把留在缓冲区的换行符丢掉:
#include <limits> // 需要包含这个头文件用numeric_limits // ...其他代码... char ch[20]; while(infile.get(ch,20)) { // 把读取操作放到循环条件里,替代!eof() cout<<ch; // 忽略直到换行符的所有字符(这里就是忽略那个留在缓冲区的\n) infile.ignore(numeric_limits<streamsize>::max(), '\n'); }
这里把!infile.eof()换成infile.get(ch,20)更安全——因为get()返回流对象,流在布尔语境下会自动检查是否处于有效状态(没失败、没坏),一旦读取失败(比如到了EOF),循环就直接停止。
方案2:直接用getline()代替get()
如果你的需求就是读取整行文本,getline()其实是更合适的选择——它会自动把换行符从缓冲区里取出来并丢弃,不会留下尾巴:
char ch[20]; while(infile.getline(ch,20)) { cout<<ch<<endl; // 因为getline丢弃了换行符,所以如果需要换行可以自己加 }
这个写法既避免了!eof()的坑,也不用手动处理换行符,逻辑更简洁。
方案3:如果一定要用!eof()(不推荐)
如果你坚持要用原来的循环条件,那需要在每次读取后检查流状态,并且清除换行符:
char ch[20]; while(!infile.eof()) { if(infile.get(ch,20)) { cout<<ch; infile.ignore(numeric_limits<streamsize>::max(), '\n'); } else { break; // 读取失败就退出循环 } }
不过这个写法不如前两种简洁,还是更推荐前两种方案。
内容的提问来源于stack exchange,提问作者Rebecca
相关产品推荐
相关产品推荐

