Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective-C code to generate a relative path given a file and a directory

Given a file path and a directory path as NSStrings, does anyone have Objective-C code to generate a path to the file, relative to the directory?

For example, given the directory /tmp/foo and the file /tmp/bar/test.txt, the code should produce ../bar/test.txt.

I know Python, at least, has a method to do this: os.path.relpath.

like image 314
Hilton Campbell Avatar asked Jun 30 '11 18:06

Hilton Campbell


People also ask

How do I create a relative path to a file?

Relative pathRelative paths make use of two special symbols, a dot (.) and a double-dot (..), which translate into the current directory and the parent directory. Double dots are used for moving up in the hierarchy. A single dot represents the current directory itself.

How do I find the relative path of a directory?

First, you have to import the os module in Python so you can run operating system functionalities in your code. Then you create the variable absolute_path which fetches the current directory relative to the root folder. This is the full path to your working directory, in this case, ~/home/projects/example-project/ .

How do I find the relative path between two directories?

Just use the relpath() function of the os module. Return a relative filepath to path either from the current directory or from an optional start directory. This is a path computation: the filesystem is not accessed to confirm the existence or nature of path or start.

What is relative path in C?

A relative path describes the location of a file relative to the current (working) directory*. An absolute path describes the location from the root directory.


1 Answers

Rather than continue to defend why I need this, I decided to just write it and share. I based this off of an implementation of Python's os.path.relpath at http://mail.python.org/pipermail/python-list/2009-August/1215220.html

@implementation NSString (Paths)

- (NSString*)stringWithPathRelativeTo:(NSString*)anchorPath {
    NSArray *pathComponents = [self pathComponents];
    NSArray *anchorComponents = [anchorPath pathComponents];

    NSInteger componentsInCommon = MIN([pathComponents count], [anchorComponents count]);
    for (NSInteger i = 0, n = componentsInCommon; i < n; i++) {
        if (![[pathComponents objectAtIndex:i] isEqualToString:[anchorComponents objectAtIndex:i]]) {
            componentsInCommon = i;
            break;
        }
    }

    NSUInteger numberOfParentComponents = [anchorComponents count] - componentsInCommon;
    NSUInteger numberOfPathComponents = [pathComponents count] - componentsInCommon;

    NSMutableArray *relativeComponents = [NSMutableArray arrayWithCapacity:
                                          numberOfParentComponents + numberOfPathComponents];
    for (NSInteger i = 0; i < numberOfParentComponents; i++) {
        [relativeComponents addObject:@".."];
    }
    [relativeComponents addObjectsFromArray:
     [pathComponents subarrayWithRange:NSMakeRange(componentsInCommon, numberOfPathComponents)]];
    return [NSString pathWithComponents:relativeComponents];
}

@end

Note that there are some cases this won't correctly handle. It happens to handle all the cases I need. Here is the skimpy unit test I used to verify correctness:

@implementation NSStringPathsTests

- (void)testRelativePaths {
    STAssertEqualObjects([@"/a" stringWithPathRelativeTo:@"/"], @"a", @"");
    STAssertEqualObjects([@"a/b" stringWithPathRelativeTo:@"a"], @"b", @"");
    STAssertEqualObjects([@"a/b/c" stringWithPathRelativeTo:@"a"], @"b/c", @"");
    STAssertEqualObjects([@"a/b/c" stringWithPathRelativeTo:@"a/b"], @"c", @"");
    STAssertEqualObjects([@"a/b/c" stringWithPathRelativeTo:@"a/d"], @"../b/c", @"");
    STAssertEqualObjects([@"a/b/c" stringWithPathRelativeTo:@"a/d/e"], @"../../b/c", @"");
    STAssertEqualObjects([@"/a/b/c" stringWithPathRelativeTo:@"/d/e/f"], @"../../../a/b/c", @"");
}

@end
like image 158
Hilton Campbell Avatar answered Sep 21 '22 13:09

Hilton Campbell