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 に答える