今天忙活了半个下午,查找正式环境上面一个程序的问题。这个程序的作用是监控文件夹,处理每一个文件,分析每个文件的每行记录,然后将这个文件拆分成两个结果文件投放到另外两个不同的目录下面去,当处理完这个文件后,将源文件剪切到备份文件夹下面去。程序的整体逻辑很简单,只用了一天的时间就完成了。可在测试工作完成后,部署到正式环境上面后,今天维护人员突然说有问题,说程序一直在处理一个文件,并且是死循环,处理的结果文件一直在增大,都已经有50多G了。我根据他的描述,说是死循环,一直在处理某个文件。然后我把这个文件取下来单独在测试环境下面测试,一切正常。说明不是文件内容的问题,我想到了文件操作时是不是磁盘坏道的问题,于是乎我一下子就想到代码里的有一个地方可能有问题。这段逻辑大概是这样的:
while(!fs.eof()) // fs的类型是std::fstream { std::string str; std::getline(fs, str); ... ... }
我就断定代码一定是在这里出问题了,你发现了吗?这个getline竟然没有返回值判断耶,so shit!,当时我这么想的,假如getline出错了,会设定ios::badbit的标记位,这个fstream就不是一个正常的IO了,我也没有做异常处理,接下来程序会一直在这个while循环里做死循环, 因为这个文件不可能会读到文件末尾,也就不会出现读到eof。 接下来我在SO(stack overflow)上面查有没有类似问题,很幸运,竟然有人遇到过类似的问题。我真的只能说 “so sorry that, I am a beginner.”
回到家后,重新打开电脑写了关于getline的程序来验证我的想法,这样做确实会导致死循环的出现(把if判断异常的代码去除)。代码如下:
#include <iostream> #include <cstdio> #include <fstream> #include <string> int main() { std::fstream fs; fs.open("/home/f_x_p/test_code/c++/testfile"); if(!fs.is_open()){ std::cerr << "file open failed!" << std::endl; return -1; } int i = 0; while(!fs.eof()) { std::string str; if(!std::getline(fs, str)){ std::cerr << "getline failed!" << std::endl; if((fs.rdstate() & std::fstream::eofbit) != 0) { continue; }else{ fs.clear(); } } std::cout << str << std::endl; if(i == 0){ fs.setstate(std::fstream::badbit); ++i; } } return 0; }
网上认识的水晶大牛说我应该没那么倒霉,这个地方出错的概率很小。结果如他所说,最终排查的结果不是代码的问题,是维护人员把配置文件搞错了。但我也很自责,配置文件出错,竟然导致了程序的死循环。我只能说 "my code is so shit."
今天真的是血的教训,一个健壮的程序真的是太重要了,以前在代码的健壮性方面确实思考的太少。