咨询:std::ifstream读取std::string为何导致C++程序死循环
尽管担心显得笨拙,我仍想咨询为何以下C++程序运行时会进入死循环:
程序代码
#include <iostream> #include <fstream> int main(int argc, char * argv[]) { std::ifstream fitData("./fitData.txt"); int line=1; while (true) { std::string compType; bool isRS; int dRecMode; double mass; fitData >> compType >> isRS >> dRecMode >> mass; if ( fitData.eof() ) break; std::cout << "at line: " << line << ":" << compType << " " << isRS << " " << dRecMode << " " << mass << std::endl; line++; if ( line > 20 ) break; } std::cout << line << std::endl; }
待读取的fitData.txt文件内容
TT: 1 7 2.26455 TT: 1 7 2.45204 TT: 1 1 2.05438 TT: 1 -5 2.36097 TT: 1 -5 2.34911 TT: 1 7 2.43344 TT: 1 3 2.5 TT: 1 1 2.34866 TT: 1 2 2.24831 TT: 1 -3 2.31099 LC: 0 99 2.27828 LC: 1 99 2.29757 LC: 2 99 2.27512 LC: 3 99 2.31149 LC: 4 99 2.31205 LC: 5 99 2.31091
程序输出
at line: 1:TT: 1 7 2.26455 at line: 2:TT: 1 7 2.45204 at line: 3:TT: 1 1 2.05438 at line: 4:TT: 1 -5 2.36097 at line: 5:TT: 1 -5 2.34911 at line: 6:TT: 1 7 2.43344 at line: 7:TT: 1 3 2.5 at line: 8:TT: 1 1 2.34866 at line: 9:TT: 1 2 2.24831 at line: 10:TT: 1 -3 2.31099 at line: 11:LC: 0 99 2.27828 at line: 12:LC: 1 99 2.29757 at line: 13:LC: 1 99 2.29757 at line: 14: 1 99 2.29757 at line: 15: 1 99 2.29757 at line: 16: 1 99 2.29757 at line: 17: 1 99 2.29757 at line: 18: 1 99 2.29757 at line: 19: 1 99 2.29757 at line: 20: 1 99 2.29757 21
该问题在WSL2(Win10 64位)、Cygwin(Win7 32位)、Xubuntu(64位)等所有测试系统中均会出现,从第13次循环开始std::string变量compType为空,后续重复输出相同内容。
技术解析
核心原因
输入格式不匹配触发流错误状态
当程序读取第13行的LC: 2 99 2.27512时,尝试将2读取到bool isRS变量。C++标准中,bool类型的输入仅接受true/false或0/1,2属于非法输入值,这会导致fitData流触发failbit(失败位),进入不可用状态。此时流的所有后续输入操作都会直接失败,但eof()仅在文件指针到达末尾时返回true,因此if (fitData.eof()) break;永远不会触发,循环持续执行。输入失败后变量保留旧值
流进入失败状态后,fitData >> compType >> isRS >> dRecMode >> mass;不会修改任何变量的值。因此后续循环中,isRS、dRecMode、mass始终保留上一次成功读取的内容(第12行的1、99、2.29757),而compType因读取失败被置为空,最终导致重复输出相同内容。循环终止条件存在缺陷
代码中的if (line > 20) break;只是临时限制了循环次数,并未解决根本问题;若文件行数超过20,死循环会持续到触发该条件。
解决方法
正确检查输入状态
将输入操作与循环条件合并,利用>>运算符返回流对象的特性,直接判断输入是否成功:int line = 1; std::string compType; int isRS_int; // 改用int存储原bool字段的值 int dRecMode; double mass; while (fitData >> compType >> isRS_int >> dRecMode >> mass) { bool isRS = isRS_int != 0; // 手动转换为bool std::cout << "at line: " << line << ":" << compType << " " << isRS << " " << dRecMode << " " << mass << std::endl; line++; }这种方式会在输入失败(包括EOF或格式错误)时直接终止循环,无需单独检查
eof()。修复bool类型输入问题
由于文件中isRS字段存在非0/1的值,不能直接用bool类型读取。建议改用int类型接收该字段,再根据业务需求手动转换为bool(比如非0值视为true)。可选:重置流状态(如需继续操作流)
如果需要在输入失败后继续使用流,可以调用fitData.clear()重置错误状态,并调用fitData.ignore()跳过错误的输入内容,但在该场景下直接终止循环更为合理。
内容的提问来源于stack exchange,提问作者troppapolvere

