根据文件和目录生成相对路径的Objective-C代码

13

给定一个文件路径和一个目录路径作为NSString,有没有Objective-C代码可以生成相对于目录的文件路径?

例如,给定目录/tmp/foo和文件/tmp/bar/test.txt,代码应该产生../bar/test.txt

我知道Python至少有一种方法可以做到这一点:os.path.relpath


4
当然,我们都可以编写一个(或挖掘一个现成的),但你的问题可能会引起警觉。你应该尝试使用NSFileManager,特别是在iOS上。苹果正在长期关闭用户对文件系统的访问,同时规范程序员如何访问文件。乱斩路径可能会导致应用程序崩溃。话虽如此,[NSString pathComponents]可以帮助你将路径分解并从那里开始。 - bshirley
注意到警告。对于我的特定用例,我仅在iOS上的应用程序沙盒内运行。我有一个完整的文件路径,需要传递给一个API,该API假定相对于特定目录的路径,并且不会接受完整路径(imageNamed)。我意识到我有工具来制作这个,但我希望有人在他们的工具包中有一个经过充分调试的版本,可以为SO访问者提供一般用途。没有重新发明轮子的意义。 - Hilton Campbell
那个轮子绝对值得重新发明,或者至少重新实现 :). 我的应用程序需要至少 iOS 4.2,所以我可以享受到他们对 imageNamed: 所做的所有错误修复。它现在具有出色的缓存行为,这就是为什么我想使用它而不是 imageWithContentsOfFile: 的原因。 - Hilton Campbell
你尝试过发送一个 ../relative/path.jpg 到 imageNamed: 吗?我很感兴趣确认它是否有效。 - bshirley
具有讽刺意味的是,我最终决定不再使用 imageNamed: 方法。我使用它的原因之一是为了缓存生成的缩略图。然而,当我需要重新生成它们时,没有办法强制使缩略图失效。 - Hilton Campbell
显示剩余2条评论
3个回答

28

我决定不再为为什么我需要这个而辩护,而是直接写下来分享。我基于Python的os.path.relpath实现了这个东西,实现过程可以参考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

请注意,有些情况下这种方法不能正确处理。但它能够处理我需要的所有情况。以下是我用来验证正确性的简略单元测试:

@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

0

你为什么不能直接使用完整路径呢?imageNamed: 完全支持这样做。根目录是你的应用程序的主要捆绑包。

myImageView.image = [UIImage imageNamed:@"/Path/To/Some/File.png"];

1
imageNamed:只接受相对于应用程序主资源束根目录的路径。 我想从我的应用程序缓存目录加载图像,该目录位于应用程序束之外。我无法使用绝对路径引用该文件(我已尝试),但我可以使用相对路径引用它(我已尝试)。谢谢你尝试帮助我找到解决方法。 - Hilton Campbell

0

这是 Hilton 代码的 Swift 版本

extension String {
    var pathComponents: [String] {
        return (self as NSString).pathComponents
    }

    static func pathWithComponents(components: [String]) -> String {
        return NSString.pathWithComponents(components)
    }

    func stringWithPathRelativeTo(anchorPath: String) -> String {
        let pathComponents = self.pathComponents
        let anchorComponents = anchorPath.pathComponents

        var componentsInCommon = 0
        for (c1, c2) in zip(pathComponents, anchorComponents) {
            if c1 != c2 {
                break
            }
            componentsInCommon += 1
        }

        let numberOfParentComponents = anchorComponents.count - componentsInCommon
        let numberOfPathComponents = pathComponents.count - componentsInCommon

        var relativeComponents = [String]()
        relativeComponents.reserveCapacity(numberOfParentComponents + numberOfPathComponents)
        for _ in 0..<numberOfParentComponents {
            relativeComponents.append("..")
        }
        relativeComponents.appendContentsOf(pathComponents[componentsInCommon..<pathComponents.count])

        return String.pathWithComponents(relativeComponents)
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接