Concatenating an already existing filename to create a new file

I had answered this question from stackoverlow and the code below is code that I wrote to explain to the OP a possible way to achieve what I believe they were looking for.

The general idea that I got from them was that they were trying to open a file with a specified file name and if that file does exist that they in turn wanted to create a new file with the same filename but with extra characters appended to the filename.

This is the small program that I wrote to mimic that behavior.

#include <string>
#include <fstream>
#include <iostream>

int main() {
std::string originalFilename("out.txt");
std::string extension(".txt");
std::string nextFilename;
bool originalFileFound = false;

// First test to see if original file exist by opening
// if so then generate the next usable name
std::fstream fileIn( originalFilename, std::ios::in );
unsigned int counter = 0;

if (fileIn.is_open()) {
counter++;
nextFilename = originalFilename.substr( 0, originalFilename.size() - extension.size() )
+ "_" + std::to_string(counter) + extension;
originalFileFound = true;
}
fileIn.close();

// Generate a few files with the appended number system to
// the end of the original filename if the original file was found.
if ( originalFileFound ) {
for ( int i = 1; i < 6; i++ ) {
std::fstream nextFile;
nextFile.open( nextFilename, std::ios::out );
if (nextFile.is_open()) {
nextFile << nextFilename; // Write this before updating...
counter++;
nextFilename = nextFilename.substr(0, nextFilename.find_first_of('_') + 1)
+ std::to_string(counter) + extension;
}
nextFile.close();
}
}
return 0;
}


I would like to know if there is a more elegant, more efficient and cleaner way to achieve this using modern C++. Writing this simple program in essence was basically self practice to improve my skills with string manipulation and the string's set of library functions and algorithms.

1 Answer

• Prefer RAII-style opening the file in the constructor. You already done that with

    std::fstream fileIn( originalFilename, std::ios::in );


so why not follow the suit with

    std::fstream nextFile(nextFilename, std::ios::out);

• There is no need to explicitly nextFile.close();. The destructor will take care of it at the end of each iteration.

• There is nothing wrong with the early return. Instead of setting originalFileFound = true;, and testing it later on, quit immediately if opening fails. That would spare a boolean flag and a level of indentation.

• Consolidate handling of nextFilename in one place. Consider

    if (!fileIn.is_open()) {
print_error_message();
return 1;
}

std::string prefix = originalFilename.substr(0, originalFilename.size() - extension.size());

for (int i = 0, counter = 0; counter < 5; i++) {
nextFilename = prefix + "_" + std::to_string(i) + extension;
....
}

• I would be very cautious to use this program, because it may overwrite existing files.

• I truly appreciate the feed back. Using a temp string for prefix does make the code look a little more cleaner and concise. And yes I know that this could potentially overwrite existing files, but as I stated it was just a demonstration program to an already existing question over at stackoverflow. I could turn this into a function that would take file manipulation flags as arguments, such as appending, overwriting etc. Feb 18 '19 at 20:42