2

在处理从 ifstream 读取的字符串后,我正在寻找一个干净的 C++ 解决方案来读取和写入同一个文件。例如,这就是我的想法

std::ofstream ofs("test.txt", std::ios_base::app);
std::ifstream ifs("test.txt");
std::string inputLine;
bool skipLine = false;

if (ofs)
{
    while (getline(ifs, inputLine))
    {
        skipLine = processLine(inputLine);

        if (!skipLine)
            ofs << inputLine << std::endl;
    }
} 

不幸的是,这不起作用。我打算读取行并在“processLine()”函数中决定我想写回或跳过它。生成的输出内容几乎是输入行的内容减去我要清除的某些行

4

2 回答 2

1

我认为你最好的选择是简单地创建一个临时文件,它应该是你的输出文件。要解释您为什么应该这样做,请阅读您的问题下方的评论。

这是一个很好的干净的例子,效果很好:

#include <string>
#include <iostream>
#include <fstream>
#include <sys/stat.h>

/*
 * @ FUNCTION: GenerateStr
 * @ PARAMETER(s):
 *      [1st - in] const size_t in_Length = This parameter
 *      take in a size_t, which should be the length of the
 *      generated std::string.
 * @ RETURN VALUE(s):
 *      This function should return the generated
 *      std::string, which was randomized during
 *      the generating process within this function.
 */
std::string GenerateStr( const size_t in_Length ) {
    const std::string Elements =
        "AaBcCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890$-_";
    std::string FileName = "";

    for( size_t Index = 0; Index != in_Length; ++ Index ) {
        //# Add random characters from Elements.
        FileName += Elements[ rand() % Elements.size() ];
    }

    return FileName;
};

/*
 * @ FUNCTION: FileExists
 * @ PARAMETER(s):
 *      [1st - in] const std::string &in_File = This parameter
 *      takes in a std::string, which should be the
 *      name of the file that will be searched for.
 * @ RETURN VALUE(s):
 *       false = The file does not exists.
 *       true = The file does exists.
 */
bool FileExists( const std::string &in_FileName ) {
    struct stat Buffer;
    return ( stat( in_FileName.c_str(), &Buffer ) != -1 );
};

/*
 * @ FUNCTION: GenerateTempFileName
 * @ PARAMETER(s):
 *      [1st - in] const size_t in_Length = This parameter
 *      takes in a constant size_t value, which should be
 *      length of temporary file name (a.k.a the returned
 *      value).
 * @ RETURN VALUE(s):
 *      This function will return a std::string, which
 *      should be a unique name for the temporary file.
 */
std::string GenerateTempFileName( const size_t in_Length ) {
    std::string TempFileName = "";
    while( FileExists( TempFileName = GenerateStr( in_Length ) + ".tmp" ) );
    return TempFileName;
};

/*
 * @ FUNCTION: OldFileToTempFile
 * @ PARAMETER(s):
 *      [1st - in] const std::string &in_OldFileName = The
 *      name of the old file.
 *      [2nd - in] const std::string &in_TempFileName = The
 *      name of the temporary file.
 * @ RETURN VALUE(s):
 *      1 = An error has occured when attempting to rename the
 *      the old file to a temporary name.
 *      -1 = An error has occured when attempting to rename the
 *      temporary file to the original name of the old file,
 *      which is in_OldFileName.
 *      -2 = This is only a warning, although this function
 *      successfully renamed the temporary file to the original
 *      name of the old file, it failed to delete the old file.
 *      0 = Successfully renamed the temporary file to
 *      in_OldFileName and deleted the old file.
 */
short OldFileToTempFile( const std::string &in_OldFileName,
                         const std::string &in_TempFileName )
{
    //# Generate a temporary name for the old file.
    const std::string OldFileTmpName = GenerateTempFileName( 15 );

    //# Rename the old file to a temporary name. Why? So we can rename
    // it back to its original name incase it failed to rename the
    // temporary file.
    if( std::rename( in_OldFileName.c_str(), OldFileTmpName.c_str() ) != 0 ) { 
        return 1;
    }

    //# If we successfully renamed the old file to a temporary name,
    //  we shall then attempt to rename the temporary file to the original
    //  name of the old file.

    //# Attempt to rename the temporary file. If we fail, we shall rename
    //  the old file back to its original name.
    if( std::rename( in_TempFileName.c_str(), in_OldFileName.c_str() ) != 0 ) {
        std::rename( OldFileTmpName.c_str(), in_OldFileName.c_str() );
        return -1;
    }

    //# Now if we successfully renamed the temporary file,
    //  we can go ahead and delete the old file. If failed,
    //  we shall return -2, which should only be a warning
    //  that we failed to delete the old file.
    if( std::remove( OldFileTmpName.c_str() ) != 0 ) {
        return -2;
    }

    return 0;
};


//# I wrote this function so it can instruct function ProcessFile to either skip
//  the current line (in_Line) or to output the line to the temporary file. But
//  mainly because, I did not know what your function processLine was actually
//  doing.
bool ProcessLine( std::string &in_Line ) {
    //# If in_Line is equal to "SkipThisLine", we shall return true,
    //  which means to skip the line. Else, it will return false,
    //  which simply means to no skip the line.
    return ( in_Line == "SkipThisLine" );
};

/*
 * @ FUNCTION: Example
 * @ DESCRIPTION:
 *   This function is an example of how to read and write a file and also
 *   to answer the question above.
 */
bool Example() {
    //# Attempt to open InFile (Test.txt).
    std::ifstream InFile( "Test.txt" );

    //# Return false if we are unable to open InFile.
    if( ! InFile.is_open() ) {
        std::cerr << "\nUnable to open file: Test.txt\n";
        return false;
    }

    //# Generate a unqiue name for the temporary file.
    const std::string TempFileName = GenerateTempFileName( 15 );

    //# Create temporary file.
    std::ofstream TempFile( TempFileName );

    std::string InLine = "";

    //# Begin processing each lines within InFile.
    while( std::getline( InFile, InLine ) ) {
        //# Process the line and check if we are suppose to skip it or not.
        if( ! ProcessLine( InLine ) ) {
            //# If we do not skip the line, we shall then write it to the
            //  temporary file (TempFile).
            TempFile << InLine << std::endl;
        }
    }

    //# Now that we are done with InFile, we can
    //  close both InFile and TempFile and continue
    //  to the next set of steps.
    InFile.close();
    TempFile.close();

    //# Attempt to rename the temporary file to Test.txt.
    short Error = OldFileToTempFile( "Test.txt", TempFileName );

    //# Check if there are no error or if the error is -2. 
    //  The -2 means that we successfully renamed the temporary
    //  file but we were unable to delete the old file.
    if( Error == 0 || Error == -2 ) {
        return true;
    }

    //# Failed to rename temporary file.
    return false;
};

int main() {

    if( Example() ) {
        std::cout << "Success!" << std::endl;
    } else { std::cout << "Failed!" << std::endl; }

    ::getchar();
    return 0;
};

请务必阅读评论,它们真的很有帮助!:)

由于@Michael Anderson 在下面的评论,我做了一些重要的改变,我们可以感谢他。

于 2013-04-11T23:57:53.680 回答
0

这是一个使用您可以使用的随机访问方法的想法。它会在最后留下尾随的旧数据,根据您的应用程序,您可以忽略或删除这些数据。是否正确取决于线条是否全部相同。但是,这应该删除您要跳过的行。

char buffer[10];
fstream fs("test.txt");
std::string inputLine;
bool skipLine = false;
long nextRead, nextWrite;
int skippedLines = 0;

if (fs)
{
    while (!fs.eof())
    {


        if (!skipLine && skippedLines == 0)
            nextWrite = fs.tellg();
        getline(fs, inputLine);

        nextRead = fs.tellg();
        if (fs.eof()) break;

        cout << inputLine << endl;
        skipLine = processLine(inputLine);

        if (!skipLine) {
            fs.seekp(nextWrite);
            fs << inputLine << std::endl;

            if (skippedLines > 0) {
                nextWrite = fs.tellp();
            }

            fs.seekg(nextRead);
            if (fs.eof()) break;
        } else {
            fs.seekg(nextRead);
            skippedLines++;
        }
    }
}
于 2013-04-12T01:15:37.223 回答