Swift相当于Ruby的"Pathname.relative_path_from"

neo*_*eye 5 url nsurl nsfilemanager swift server-side-swift

Ruby的Pathname.relative_path_from文档.

在objc中已经有KSFileUtilities的ks_stringRelativeToURL方法,非常接近.我正在寻找可以在Linux上运行的纯粹快速解决方案.

我更喜欢使用file://URL的解决方案,但String也很好.

文件系统可以区分大小写/不区分大小写.确定相对路径可能很棘手.

输入和预期输出的示例:

| Long Path                      | Relative to Path | Return Value      |
|--------------------------------|------------------|-------------------|
| /usr/X11/agent/47.gz           | /usr/X11         | agent/47.gz       |
| /usr/share/man/meltdown.1      | /usr/share/cups  | ../man/meltdown.1 |
| file:///var/logs/x/y/z/log.txt | file:///var/logs | x/y/z/log.txt     |
Run Code Online (Sandbox Code Playgroud)

Swift已经有了FileManager.getRelationship(_:of:in:toItemAt :),但它没有返回相对路径.

Mar*_*n R 7

据我所知,在Swift标准库或Foundation框架中没有这样的方法.

以下是作为扩展方法的可能实现URL:

extension URL {
    func relativePath(from base: URL) -> String? {
        // Ensure that both URLs represent files:
        guard self.isFileURL && base.isFileURL else {
            return nil
        }

        // Remove/replace "." and "..", make paths absolute:
        let destComponents = self.standardized.pathComponents
        let baseComponents = base.standardized.pathComponents

        // Find number of common path components:
        var i = 0
        while i < destComponents.count && i < baseComponents.count
            && destComponents[i] == baseComponents[i] {
                i += 1
        }

        // Build relative path:
        var relComponents = Array(repeating: "..", count: baseComponents.count - i)
        relComponents.append(contentsOf: destComponents[i...])
        return relComponents.joined(separator: "/")
    }
}
Run Code Online (Sandbox Code Playgroud)

我的测试代码:

func test(_ p1: String, _ p2: String) {
    let u1 = URL(fileURLWithPath: p1)
    let u2 = URL(fileURLWithPath: p2)
    print(u1.relativePath(from: u2) ?? "<ERROR>")
}
test("/usr/X11/agent/47.gz",      "/usr/X11")        // "agent/47.gz"
test("/usr/share/man/meltdown.1", "/usr/share/cups") // "../man/meltdown.1"
test("/var/logs/x/y/z/log.txt",   "/var/logs")       // "x/y/z/log.txt"
Run Code Online (Sandbox Code Playgroud)

备注:

  • "" 删除给定URL中的"..",并使相对文件URL成为绝对URL(均使用standardized方法URL).
  • 处理案例(in)敏感度.
  • 假设基本URL表示目录.

附录: @neoneye将它包装成Swift包: SwiftyRelativePath.