将文本文件解析为列表会导致分段错误

Posted

技术标签:

【中文标题】将文本文件解析为列表会导致分段错误【英文标题】:Parsing text file into list gives segmentation fault 【发布时间】:2013-11-26 12:16:07 【问题描述】:

我在尝试解析大文本文件时遇到分段错误。该文件包含 91 529 个 mRNA 转录本和有关这些转录本的详细信息。我创建了一个 RefSeqTranscript 对象,它将获取这些详细信息。当我解析文件时,我创建了这些对象的列表,并开始将详细信息放入这些列表中。它适用于前 1829 个转录本,然后因分段错误而崩溃。我正在运行的方法是:

void TranscriptGBFFParser::ParseFile(list<RefSeqTranscript> &transcripts, const char* filepath)

    cout << "Parsing " << filepath << "..." << endl;

    ifstream infile;
    infile.open(filepath);

    int num = 0;
    RefSeqTranscript *transcript = new RefSeqTranscript();
    for(string line; getline(infile, line); )
    
        in.clear();
        in.str(line);

        if (boost::starts_with(line, "LOCUS"))
        
            if((*transcript).transcriptRefSeqAcc.size() > 0)
                       
                cout << (*transcript).transcriptRefSeqAcc << ":" << (*transcript).gi << ":" << (*transcript).gene.geneName << ":" << ++num << endl; 

                transcripts.push_back(*transcript); 
                delete transcript;

                RefSeqTranscript *transcript = new RefSeqTranscript();  

               
        
        else if (boost::starts_with(line, "     var"))
        
            TranscriptVariation variant;
            (*transcript).variations.push_back(variant);            
        
        //Store the definition of the transcript in the description attribute
        else if (boost::starts_with(line, "DEFINITION"))
                   
            (*transcript).description = line.substr(12);

            for(line; getline(infile, line); )
            
                if(boost::starts_with(line, "ACCESSION   "))
                    break;

                (*transcript).description += line.substr(12);
                   
        
        //The accession number and GI number are obtained from the VERSION line
        else if (boost::starts_with(line, "VERSION"))
        
            string versions = line.substr(12);
            vector<string> strs;
            boost::split(strs, versions, boost::is_any_of( " GI:" ), boost::token_compress_on);
            boost::trim_left(strs[0]);

            (*transcript).transcriptRefSeqAcc = strs[0];
            (*transcript).gi = atoi(strs[1].c_str());
        
        //Gene information is obtained from the "gene" sections of each transcript
        else if (boost::starts_with(line, "     gene"))
                   
            for(line; getline(infile, line); )
            
                if(boost::starts_with(line.substr(21), "/gene="))
                
                    Gene *gene = new Gene();

                    string name = line.substr(27);
                    Utilities::trim(name, '\"');

                    (*gene).geneName = name;

                    (*transcript).gene = *gene;

                    delete gene;
                    break;
                
            
            (*transcript).gene.geneID = 0;      
        
        else if (boost::starts_with(line, "     CDS"))
        
            (*transcript).proteinRefSeqAcc = "";            
        
        else if (boost::starts_with(line, "ORIGIN"))
        
            (*transcript).sequence = "";            
               
    

    cout << (*transcript).transcriptRefSeqAcc << ":" << (*transcript).gi << ":" << (*transcript).gene.geneName << endl;

    transcripts.push_back(*transcript); 
    delete transcript;          

    cout << "No. transcripts: " << transcripts.size() << endl;
    cout << flush;

    infile.close();

    cout << "Finished parsing " << filepath << "." << endl; 

我是 C++ 新手,对如何使用指针等不太了解,所以我猜我可能在那里做错了什么。我不明白为什么它会在切割之前适用于近 2000 个对象。

我正在解析的文件为 2.1 GB,由大约 44 000 000 行组成,因此对于如何提高效率的任何提示也将不胜感激。

【问题讨论】:

where 在您显示的代码中它是否因分段错误而停止?使用您选择的调试器(gdb、visual studio)运行程序,并报告它失败的行号。还有,那条线 1829/1830 有什么特别之处吗?也许这是根据解析代码似乎第一次出现的一种线型? 你的盒子有多少内存?您可能内存不足,其中一个分配失败并返回 NULL。 为什么最后用transcript = new RefSeqTranscript复制?仅使用堆栈上的对象,例如 RefSeqTranscript transcript 感谢您的帮助,SB 的回答解决了问题。也就是说,我需要大量阅读堆和堆栈以及何时/如何使用它们。 除非您需要对象超过范围,否则请使用堆栈。如果需要std::shared_ptrmake_shared&lt;&gt;,或者shared_array等......然后,如果你不能使用以前的解决方案,请使用原始指针和new。 【参考方案1】:

这可能不是唯一的答案,但你有一个泄漏......

    if (boost::starts_with(line, "LOCUS"))
    
        if((*transcript).transcriptRefSeqAcc.size() > 0)
                   
            cout << (*transcript).transcriptRefSeqAcc << ":" << (*transcript).gi << ":" << (*transcript).gene.geneName << ":" << ++num << endl; 

            transcripts.push_back(*transcript); 
            delete transcript;
            // LEAK!
            RefSeqTranscript *transcript = new RefSeqTranscript();  

           
    

你的意思可能是:

transcript = new RefSeqTranscript();

【讨论】:

【参考方案2】:

除非您提供更多详细信息,否则很难说出具体的内容:

它在哪一行崩溃了? 您真的同时需要所有这些成绩单吗?

但我建议您进行一些改进:

不要对RefSeqTranscript *transcript 使用指针(或至少使用智能指针); 不要将指针用于Gene *gene; 一般来说,除非确实需要,否则不要使用指针;

你这里有一个错误:

   delete transcript;

   RefSeqTranscript *transcript = new RefSeqTranscript(); 

由于您已经在循环主体之外声明了成绩单,因此您在此处使用具有相同名称的新变量将其隐藏。这会导致内存泄漏,此外,您删除了外部转录本并且不使用任何内容替换它。因此,您可能会在下一次迭代中崩溃。

【讨论】:

谢谢,您指出的错误解决了这个问题。不使用指针的原因是什么? @DavidBrown 每次使用指针时,都存在潜在的内存泄漏。当你使用一个生命周期由编译器管理的对象时,你甚至没有机会造成内存泄漏。 除了使用原始指针的危险之外,还有与堆内存分配相关的一定开销。

以上是关于将文本文件解析为列表会导致分段错误的主要内容,如果未能解决你的问题,请参考以下文章

将文本文件从 Android 上传到 Rails 会导致内容类型欺骗错误

单击时将文本添加到鼠标位置时会出现分段错误

GLib 正则表达式匹配在特定匹配和模式上给出分段错误

将文件中的值读入数组会导致分段错误,C

从文本文件中读取行时出现字符串分段错误

将 Excel 2007 中的文本写入 Sharepoint 站点上的 .txt 文件会导致运行时错误“76”找不到路径