24

How would I rename a file, keeping the file in the same directory?

I have a string containing a full path to a file, and a string containing a the new filename (and no path), for example:

NSString *old_filepath = @"/Volumes/blah/myfilewithrubbishname.avi";
NSString *new_filename = @"My Correctly Named File.avi";

I know about NSFileManager's movePath:toPath:handler: method, but I cannot workout how to construct the new file's path..

Basically I'm looking for the equivalent to the following Python code:

>>> import os
>>> old_filepath = "/Volumes/blah/myfilewithrubbishname.avi"
>>> new_filename = "My Correctly Named File.avi"
>>> dirname = os.path.split(old_filepath)[0]
>>> new_filepath = os.path.join(dirname, new_filename)
>>> print new_filepath
/Volumes/blah/My Correctly Named File.avi
>>> os.rename(old_filepath, new_filepath)
Martin Gordon
  • 36,329
  • 7
  • 58
  • 54
dbr
  • 165,801
  • 69
  • 278
  • 343

5 Answers5

36

NSFileManager and NSWorkspace both have file manipulation methods, but NSFileManager's - (BOOL)movePath:(NSString *)source toPath:(NSString *)destination handler:(id)handler is probably your best bet. Use NSString's path manipulation methods to get the file and folder names right. For example,

NSString *newPath = [[oldPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:newFilename];
[[NSFileManager defaultManager] movePath:oldPath toPath:newPath handler:nil];

Both classes are explained pretty well in the docs, but leave a comment if there's anything you don't understand.

Marc Charbonneau
  • 40,399
  • 3
  • 75
  • 82
  • Aha, I was missing the stringBy___PathComponents methods, thanks! – dbr May 16 '09 at 23:53
  • 18
    movePath:toPath:handler: is deprecated in favor of moveItemAtPath:toPath:error:, which, if it fails, will actually tell you *why* it failed. – Peter Hosey May 16 '09 at 23:59
  • Hi, @Marc Charbonneau .I know this answer is wonderful.But a question is that I want the "newFilename" have a "/".Example,I want rename "123.mp3" to "12/3.mp3",It would not work.I think NSFileManager see the "/" as the path. – Vienta Jun 20 '14 at 07:28
  • Deprecated since OS X 10.5 – Aviram Netanel Apr 20 '16 at 06:49
14

It's worth noting that moving a file to itself will fail. I had a method that replaced spaces with underscores and made the file name lowercase and renamed the file to the new name. Files with only one word in the name would fail the rename as the new name would be identical on a case-insensitive file system.

The way I resolved this was to do a two step rename, first renaming the file to a temporary name and then renaming it to the intended name.

Some pseudocode explaining this:

NSString *source = @"/FILE.txt";
NSString *newName = [[source lastPathComponent] lowercaseString];
NSString *target = [[oldPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];

[[NSFileManager defaultManager] movePath:source toPath:target error:nil]; // <-- FAILS

The solution:

NSString *source = @"/FILE.txt";
NSString *newName = [[source lastPathComponent] lowercaseString];

NSString *temp = [[oldPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@-temp", newName]];
NSString *target = [[oldPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:newName];

[[NSFileManager defaultManager] movePath:source toPath:temp error:nil];
[[NSFileManager defaultManager] movePath:temp toPath:target error:nil];
Martin Gordon
  • 36,329
  • 7
  • 58
  • 54
  • 2
    Well, I'd say that just checking if the new and old names are equal before moving is probably better for performance than moving a file twice. – 11684 Mar 30 '13 at 21:00
  • 1
    But that wouldn't accomplish the goal of changing the filename's case. – Joe Strout Aug 07 '13 at 01:10
8

I just wanted to make this easier to understand for a newbie. Here's all the code:

    NSString *oldPath = @"/Users/brock/Desktop/OriginalFile.png";
NSString *newFilename = @"NewFileName.png";

NSString *newPath = [[oldPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:newFilename];
[[NSFileManager defaultManager] movePath:oldPath toPath:newPath handler:nil];

NSLog( @"File renamed to %@", newFilename );
Brock Woolf
  • 46,656
  • 50
  • 121
  • 144
4

here's a more recent example for iOS, the NSFileManager method is a little different:

NSString *newFilename = [NSString stringWithFormat:@"%@.m4a", newRecording.title];

NSString *newPath = [[newRecording.localPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:newFilename];
[[NSFileManager defaultManager] moveItemAtPath:newRecording.localPath toPath:newPath error:nil];
Matjan
  • 3,591
  • 1
  • 33
  • 31
0

For the icing on top, a category on NSFileManager:

@implementation NSFileManager (FileManipulations)


- (void)changeFileNamesInDirectory:(NSString *)directory changeBlock:(NSString * (^) (NSString *fileName))block
{
    NSString *inputDirectory = directory;

    NSFileManager *fileManager = [NSFileManager new];

    NSArray *fileNames = [fileManager contentsOfDirectoryAtPath:inputDirectory error:nil];
    for (NSString *fileName in fileNames) {

        NSString *newFileName =  block(fileName);

        NSString *oldPath = [NSString stringWithFormat:@"%@/%@", inputDirectory, oldFileName];
        // move to temp path so case changes can happen
        NSString *tempPath = [NSString stringWithFormat:@"%@-tempName", oldPath];
        NSString *newPath = [[oldPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:newFileName];

        NSError *error = nil;
        [fileManager moveItemAtPath:oldPath toPath:tempPath error:&error];
        if (error) {
            NSLog(@"%@", [error localizedDescription]);
            return;
        }
        [fileManager moveItemAtPath:tempPath toPath:newPath error:&error];
        if (error) {
            NSLog(@"%@", [error localizedDescription]);
        }
    }
}


@end
Alec
  • 483
  • 8
  • 20