Algorithm to Obtain Destination File in moveFileTo()


#1

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.


#3

something like this should do it:

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


#4

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;
}

#5

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.


#6

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


#7

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.


#8

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.


#9

you can use getSiblingFile() for that:

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

#10

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.


#11

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


#12

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.


#13

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.


#15

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


#16

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"


#17

not sure what you’re saying here…??


#18

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


#19

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


#20

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.


#21

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


#22

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…