Algorithm to Obtain Destination File in moveFileTo()

I understand that using the moveFileTo() function is the way to rename a file, like using mv in the shell to rename a file, but my question is, what is a decent algorithm to obtain the “destination” file path?

Let me explain,

I want to change the name of a file in a given folder, but I want it to keep the exact same extension as before.

Ex.

I want to change:
/Users/USERNAME/Desktop/Files/sound.wav

to:
/Users/USERNAME/Desktop/Files/noise.wav

The extension shouldn’t matter, it should always be the same after as it was before. How can I go about this?

I attempted this, but it seems super long-winded and for some reason, some assignments are not working:

CustomFile fullPath = customListBoxOnePtr->getHighlightedFile();
CustomFile parentDirectory = fullPath.getParentDirectory();
String extension = fullPath.getFileExtension();

fullPath = parentDirectory; // This assignment doesn't work
String newPath = fullPath.getFullPathName();
newPath += "/New File Name";
newPath += extension;
fullPath = newPath // This assignment doesn't work

I’m not sure how to add on to a file path without assigning it to a string and manipulating that, so any suggestions would be greatly appreciated. I feel like this shouldn’t be as difficult as I’m making it.

something like this should do it:

ext = File( path ).getFileExtension();
path.replace( File( path ).getFileName(), new_name + ext );

You can manipulate the path with other File methods. For example:

int main (int argc, char* argv[])
{
    File fullPath ("/Users/USERNAME/Desktop/Files/sound.wav");
    
    auto extension = fullPath.getFileExtension();
    auto parent    = fullPath.getParentDirectory();
    
    auto newPath   = parent.getChildFile ("noise")
                           .withFileExtension (extension);

    jassert (newPath.getFullPathName() == "/Users/USERNAME/Desktop/Files/noise.wav");
    
    Logger::writeToLog (newPath.getFullPathName());

    return 0;
}
1 Like

I’ll dig into this, but I do have one question, that String method, replace(), does that just replace all identical occurrences of the substring? So if a user had:

/User/USERNAME/Audio/Audio.wav

and used replace("Audio", "Sound");

wouldn’t they get:

/User/USERNAME/Sound/Sound.wav?
I wouldn’t be silly enough to name my folders that way, but I never know what another person might do.
I may be interpreting it all wrong though.

And are these due to something wrong with whatever your CustomFile class is?

Here is my assignment operator in my CustomFile class:

CustomFile& CustomFile::operator= (const File &input)
{
    (File) *this = input;
    
    return *this;
}

I just wanted the incoming File to load into my File base class. If I don’t provide this overloaded assignment operator, my constructor is automatically called instead, and I don’t want that.

So you’re inheriting from File? If so, that has been marked as final in the develop branch so you’ll need to rethink that anyway.

you can use getSiblingFile() for that:

auto extension = oldFile.getFileExtension();
auto newFile = oldFile.getSiblingFile ("newName").withFileExtension (extension);
1 Like

I absolutely am inheriting from File. I didn’t know about any of this. Wonder if there’s some sort of list of “do not inherit these classes,” so that we don’t spend time on something that won’t ever work.

that’s why the replace() replaces the full filename including extension, not just the name part, i.e. it will only ever replace “Audio.wav” with “Sound.wav” and not match anything else

Gotcha! I’ll dig into this and the other responses so far, but it looks like i may be hitting a roadblock, since I can’t reassign my initial CustomFile path since we are not allowed to inherit them now I guess.

I think it being marked as final illustrates the kinds of problems you can run into with inheritance:

CustomFile& CustomFile::operator= (const File& input)
{
    (File) *this = input; ///<< This constructs a temporary File object
                          ///   which is a copy of your object,
                          ///   which is assigned the incoming value,
                          ///   which is then destoryed...
    return *this;         ///   ... and you return that value you had in the first place
}

If you cast it to a File& then it might work, but you should definitely be using static_cast rather than C-style casts. But again, you shouldn’t be inheriting from File anyway, there will almost certainly be a cleaner solution for what your CustomFile class achieves.

What about instead of inheriting File, I make a private variable of type File and just make setter / getter functions?

Manipulating the File is safer… Slightly contrived, but quite valid, example:

int main (int argc, char* argv[])
{
    File fullPath ("/Users/USERNAME/Desktop/Files/sound.wavFiles/sound.wav");
    
    auto extension = fullPath.getFileExtension();

    auto path = fullPath.getFullPathName();
    auto newPath = path.replace (fullPath.getFileName(), "noise" + extension);
    
    jassert (newPath == "/Users/USERNAME/Desktop/Files/sound.wavFiles/noise.wav"); // ouch!
    
    Logger::writeToLog (newPath);
    
    return 0;
}

We end with: "/Users/USERNAME/Desktop/Files/noise.wavFiles/noise.wav"

not sure what you’re saying here…??

Sorry, I’m just saying that doing String::replace() might not, in some cases, only match the file name.

still confused, what is the type of path, that you call replace() on, if not String? Our code is the same…

It is a String. I’m just saying that there is a potential edge case where the whole filename, including the extension, could legally appear within one of the parent directory names. And String::replace() replaces all instances. If there was a String::replaceLastOccurrenceOf() then that might be OK (there isn’t) but I’m sure Jules would recommend using the File methods for this task.

1 Like

yes, totally agree with that. you posted a response though so I was looking for that to contain a solution, but I see now that it didn’t - that’s what confused me

Well, I no longer am inheriting File, instead, my CustomFile class now has a private File member, with getter and setter functions.

Assigning is as simple as calling the getter method to the JUCE::File and then = newPath;

And yet, I still cannot assign a new path using one of my objects on the left side of the = operator and a string or file on the right side of it. Even if I switch from CustomFile to File and try to assign a path to it, it doesn’t work.

The only way I can assign a path is by using a new File or CustomFile instance…

What the heck…