记一次程序排错与std::getline

今天忙活了半个下午,查找正式环境上面一个程序的问题。这个程序的作用是监控文件夹,处理每一个文件,分析每个文件的每行记录,然后将这个文件拆分成两个结果文件投放到另外两个不同的目录下面去,当处理完这个文件后,将源文件剪切到备份文件夹下面去。程序的整体逻辑很简单,只用了一天的时间就完成了。可在测试工作完成后,部署到正式环境上面后,今天维护人员突然说有问题,说程序一直在处理一个文件,并且是死循环,处理的结果文件一直在增大,都已经有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."

今天真的是血的教训,一个健壮的程序真的是太重要了,以前在代码的健壮性方面确实思考的太少。

时间: 2024-10-07 04:17:22

记一次程序排错与std::getline的相关文章

【转】java线上程序排错经验2 - 线程堆栈分析

前言 在线上的程序中,我们可能经常会碰到程序卡死或者执行很慢的情况,这时候我们希望知道是代码哪里的问题,我们或许迫切希望得到代码运行到哪里了,是哪一步很慢,是否是进入了死循环,或者是否哪一段代码有问题导致程序很慢,或者出现了线程不安全的情况,或者是某些连接数或者打开文件数太多等问题,总之我们想知道程序卡在哪里了,哪块占用了大量的资源. 此时,或许通过线程堆栈的分析就能定位出问题. 如果能深入掌握堆栈分析的技术,很多问题都能迎刃而解,但是线程堆栈分析并不简单,设计到线上的排错问题,需要有一定的知识

记一次程序严重卡顿的经历

前几天在电脑上VS的安装后发现有一程序,叫applicaton verifier是用来记录某一个应用程序执行情况的工具,偶然就将工作中所使用的一个开后程序引入其中,当时并没有仔细研究如何使用. 过了几天因为工作上要使用此程序,启动后发现这个程序启动后Cpu占用非常严重,以致程序中SendKeys在发送Tab键时,这个程序就无响应挂掉了,无耐之下翻源代码定位问题,结果两句代码之间执行居然要30秒左右,无解了,后来将此程序执行文件重新命名后,居然一切正常了. 后来在Windows开始程序中发现这个程

TFboy养成记 简单小程序(Variable &amp; placeholder)

学习参考周莫烦的视频. Variable:主要是用于训练变量之类的.比如我们经常使用的网络权重,偏置. 值得注意的是Variable在声明是必须赋予初始值.在训练过程中该值很可能会进行不断的加减操作变化. placeholder:也是用于存储数据,但是主要用于feed_dict的配合,接收输入数据用于训练模型等.placeholder值在训练过程中会不断地被赋予新的值,用于批训练,基本上其值是不会轻易进行加减操作. placeholder在命名时是不会需要赋予值得,其被赋予值得时间实在feed_

记一次zabbix排错过程

zabbix用了也有一段时间,基本上大大小小的报错都接触过.我个人有个习惯,偶尔会去tail -f 一下zabbix_server的日志,可能是强迫症的关系,不喜欢看到日志有任何报错信息. 本来10.1应该放假,但是临回家前一天的时候是突然发现日志中频繁曝出以下这条报错. zabbix_server [4615]: ERROR [file:json.c,line:714] Something impossible has just happened. zabbix_server [4615]:

C++ 用于大型程序的工具

<C++ Primer 4th>读书笔记 相对于小的程序员团队所能开发的系统需求而言,大规模编程对程序设计语言的要求更高.大规模应用程序往往具有下列特殊要求: 1. 更严格的正常运转时间以及更健壮的错误检测和错误处理.错误处理经常必须跨越独立开发的多个子系统进行. 2. 能够用各种库(可能包含独立开发的库)构造程序. 3. 能够处理更复杂的应用概念. C++ 中有下列三个特征分别针对这些要求:异常处理.命名空间和多重继承. 异常处理 通过异常我们能够将问题的检测和问题的解决分离,这样程序的问题

程序实践:定义具有成员函数的类

现在从一个由GradeBook类和main函数组成的例子说起,此例是一系例循序渐进例子中的第一个,这些例子通过后续博文讲解,最终是一个功能众多的GradeBook类. 定义具有无参数的成员函数 这里,GradeBook类表示可供教师管理学生考试成绩的成绩簿,而在main函数创建了一个GradeBook对象.main函数使用这个对象和它的成员函数,在屏幕上显示一条欢迎教师进入成绩簿程序的信息. PS:关键字class后跟类名GradeBook.按照惯例,用户定义的类名字以大写字母开头,而且为了增强

并行程序模拟

任务介绍 你的任务是模拟n个程序的并行运算.(按照输入编号为1~n)的并行执行. 代码实现 #define LOCAL #include<bits/stdc++.h> using namespace std; int main(){ #ifdef LOCAL freopen("data.in","r",stdin); freopen("data.out","w",stdout); #endif // LOCAL i

2.一个简单的c++程序。

每个程序员的Hello World程序 //This is a small c++ program #include <iostream> int main() { std::cout << "Hello, World" << std::endl; system("pause"); return 0; } 1.注释 符号//开始,到行末都是注释.编译器忽略注释.写注释的原因是:告诉程序相关信息,方便理解. 2.#include 在

拜师鸟哥之linux学习体会(9)——vim程序编辑器

1.    在linux下也是可以编程的哦,采用的是vim程序编辑器,如果你学会使用vim并能看懂一个vim程序,那就很了不得了.在介绍vim之前,我们先学习下他的前身vi.vi具有三种模式:一般模式.编辑模式和指令列命令模式.三者之间是这样转换的: 2.    一般打开一个vim程序就进入一般模式,之后输入o,O,a,A,i,I,r,R中的任何一个就进入编辑模式,之后左下方会出现INSERT或REPLACE.若要回到一般模式,就必须按下Esc来退出编辑模式.在一般模式下,输入:/?中的任意一个