来自cpp文件的部分行以输出文件结尾 - 闹鬼的代码?

时间:2013-01-27 20:59:12

标签: c++ c file printf

对不起,制作一个完全可重现的错误版本是非常困难的 - 所以请用我的原理图代码来表示。

该程序从网页检索信息,处理它,并将输出保存到ASCII文件。我还有一个“日志”文件(FILE *theLog ---包含在Manager对象中),用于报告错误等。


一些背景方法:

// Prints string to log file
void Manager::logEntry(const string lstr) {
    if( theLog != NULL ) { fprintf(theLog, "%s", lstr.c_str()); }
}

// Checks if file with given name already exists
bool fileExists(const string fname) {
    FILE *temp;
    if( temp = fopen(fname.c_str(), "r") ) { 
        fclose(temp);
        return true;
    } else { return false; }
}

// Initialize file for writing (some components omitted)...
bool initFile(FILE *&oFile, const string fname) {
    if(oFile = fopen(fname.c_str(), "w") ) { return true; }
    else { return false; }
}

造成麻烦的事情:

// Gets data from URL, saves to file 'dataFileName', input control flag 'foreCon'
//                     stu is some object that has string which i want
bool saveData(Manager *man, Stuff *stu, string dataFileName, const int foreCon) {
    char logStr[CHARLIMIT_LARGE];          // CHARLIMIT_LARGE = 2048
    sprintf(logStr, "Saving Data...\n");
    man->logEntry( string(logStr) );       // This appears fine in 'theLog' correctly

    string data = stu->getDataPrefixStr() + getDataFromURL() + "\n";        // fills 'data' with stuff
    data += stu->getDataSuffixStr();

    if( fileExists(dataFileName) ) {
        sprintf(logStr, "save file '%s' already exists.", dataFileName.c_str() );
        man->logEntry( string(logStr) );
        if( foreCon == -1 ) {
            sprintf(logStr, "foreCon = %d, ... exiting.", foreCon);        // LINE 'A' : THIS LINE ENDS UP IN OUTPUT FILE
            tCase->logEntry( string(logStr) );
            return false;
        } else {
            sprintf(logStr, "foreCon = %d, overwriting file.", foreCon);   // LINE 'B' : THIS LINE ENDS UP IN LOG FILE
            tCase->logEntry( string(logStr) );
        }                                                                                                 
    }

    // Initialize output file
    FILE *outFile;
    if( !initFile(outFile, dataFileName) ) {
        sprintf(logStr, "couldn't initFile '%s'", dataFileName.c_str());
        tCase->logEntry( string(logStr) );
        return false;
    }

    fprintf(outFile, "%s", data.c_str());                 // print data to output file

    if( fclose(outFile) != EOF) {
        sprintf(logStr, "saved to '%s'", dataFileName.c_str());
        tCase->logEntry( string(logStr) );
        return true;
    }

    return false;
}

如果文件已经存在,AND'int foreCon = -1'那么代码应该打印出'A'到logFile的行。如果文件存在且foreCon!= -1,则旧文件将被data覆盖。如果该文件不存在,则创建该文件,并将数据写入该文件。

然而,结果是,数据文件中出现了'A'行的分解版本,并且在日志文件中打印了'B'行!!!!

数据文件的样子:

.. exiting.20130127 161456
20130127 000000,55,17,11,0.00
20130127 010000,54,17,11,0.00
... ...

第二行和前面看起来是正确的,但是有一条额外的行包含'A'行的一部分。

现在,真正的肮脏部分。如果我在if( foreCon == -1) { ... }块中注释掉所有内容,则数据文件如下所示:

%d, ... exiting.20130127 161456
20130127 000000,55,17,11,0.00
20130127 010000,54,17,11,0.00
... ...

还有一个额外的行,但它是复制到数据文件中的LITERAL CODE。

我认为我的代码中有一个吵闹的人。我不明白这是怎么发生的。


编辑:我已尝试打印以控制data字符串,并且它会提供相同的混乱值:即%d, ... exiting.20130127 161456 - 所以它必须是string而不是FILE * {{1}}

3 个答案:

答案 0 :(得分:2)

根据您的最新评论回答:

  

getDataPrefixStr()最终返回一个以。开头的字符串   像字符串retStr = COMCHAR +“在...上创建的文件”;这样   const char COMCHAR ='#';。 COMCHAR可能是问题吗?

您不能像这样添加字符和字符串文字(char的数组,而不是string s)。

您正在将“(#”的ASCII)添加到“在...上创建的文件”的地址,即getDataPrefixStr()是从该字符串的开头起35个字符的任何内容。由于所有文字字符串都存储在同一数据区域中,因此您将从输出中的程序中获取字符串。

相反,你冷酷的

   const string COMCHAR = "*"; 
   string retStr = COMCHAR + " file created on ...";

答案 1 :(得分:1)

可能是logStr太短并且导致数据被其他缓冲区覆盖(您是否仔细检查CHARLIMIT_LARGE?)。您可以通过评论对logStrsprintf)的所有写入来诊断此问题,并查看data是否仍然已损坏。通常,如果用户可以设置dataFileName(是一个非常长的字符串),那么您的代码很容易受到攻击;请改用snprintfostringstream

否则,我猜是stu->getDataPrefixStr()getDataFromURL()返回损坏的结果或返回类型char*而不是string。尝试直接将这些值打印到控制台,以查看它们是否已损坏。如果他们返回char*,则data = stu->getDataPrefixStr() + getDataFromURL()将有未定义的行为。

答案 2 :(得分:-1)

if( temp = fopen(fname.c_str(), 'r') ) { 

应该是

if( temp = fopen(fname.c_str(), "r") ) {