Given a file path and a directory path as NSString
s, 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
.
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.
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/ .
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With