首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >快速相当于Ruby的"Pathname.relative_path_from“

快速相当于Ruby的"Pathname.relative_path_from“
EN

Stack Overflow用户
提问于 2018-01-20 01:46:05
回答 3查看 1.9K关注 0票数 7

Ruby的从…文档。

在objc中,已经有了KSFileUtilities的stringRelativeToURL方法,这非常接近。我正在寻找一种能够在Linux上运行的纯快速解决方案。

我更喜欢使用file:// URL的解决方案,但String也不错。

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

投入和预期产出实例:

代码语言:javascript
复制
| 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     |

Swift已经有了::其中:在:),但是它不返回相对路径。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-01-20 20:12:48

据我所知,Swift标准库或基金会框架中没有这样的方法。

下面是作为URL扩展方法的一种可能实现

代码语言:javascript
复制
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: "/")
    }
}

我的测试代码:

代码语言:javascript
复制
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"

备注:

  • ".“还有“.”在给定的URL中,删除相对文件URL,并将相对文件URL变为绝对URL(都使用standardized方法URL)。
  • 不处理大小写敏感性。
  • 假定基本URL表示一个目录。

增编:@neoneye将其包装成一个Swift包:SwiftyRelativePath

票数 10
EN

Stack Overflow用户

发布于 2019-05-09 07:13:51

马丁·R得到了正确的答案。但是,当基本URL本身是一个文件时,我遇到了一个问题。因此,我做了一些调整:

代码语言:javascript
复制
func relativePath(from base: URL) -> String? {
    // Ensure that both URLs represent files:
    guard self.isFileURL && base.isFileURL else {
        return nil
    }

    //this is the new part, clearly, need to use workBase in lower part
    var workBase = base
    if workBase.pathExtension != "" {
        workBase = workBase.deletingLastPathComponent()
    }

    // Remove/replace "." and "..", make paths absolute:
    let destComponents = self.standardized.resolvingSymlinksInPath().pathComponents
    let baseComponents = workBase.standardized.resolvingSymlinksInPath().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: "/")
}

我的测试用例有点扩展了。案例4是我引发这一小变化的诱因。

代码语言:javascript
复制
 func testRelativePath() {
    // This is an example of a functional test case.
    // Use XCTAssert and related functions to verify your tests produce the correct results.
    func test(_ p1: String, _ p2: String,_ result: String,_ nr: Int) {
        let u1 = URL(fileURLWithPath: p1)
        let u2 = URL(fileURLWithPath: p2)
        let r = u1.relativePath(from: u2)!
        XCTAssert( r == result,"\(nr): '\(r)' != '\(result)'")
    }
    test("/usr/X11/agent/47.gz",      "/usr/X11","agent/47.gz", 1)
    test("/usr/share/man/meltdown.1", "/usr/share/cups", "../man/meltdown.1",2 )
    test("/var/logs/x/y/z/log.txt",   "/var/logs", "x/y/z/log.txt",3)
    test("/usr/embedded.jpg",   "/usr/main.html", "embedded.jpg",4)
    test("/usr/embedded.jpg",   "/usr", "embedded.jpg",5)
    test("~/Downloads/resources",   "~/", "Downloads/resources",6)
    test("~/Downloads/embedded.jpg",   "~/Downloads/main.html", "embedded.jpg",7)
    test("/private/var/logs/x/y/z/log.txt", "/var/logs", "x/y/z/log.txt",8)
 }
票数 4
EN

Stack Overflow用户

发布于 2020-02-12 12:00:45

我正在使用Kneup向导的版本,但是当基本dir有一个扩展时出现了问题。因此,我添加了代码来检查路径是否存在,并且是dir。

代码语言:javascript
复制
public extension URL 
{
    func relativePath(from base: URL) -> String?
    {
        // Ensure that both URLs represent files:
        guard self.isFileURL &&
              FileManager.default.fileExists(atPath: self.path) else
        {
            NSLog("self is not a fileURL or it does not exists")
            return nil
        }

        var isDir = ObjCBool(true)
        guard FileManager.default.fileExists(atPath: base.path, isDirectory: &isDir) &&
              isDir.boolValue else
        {
            NSLog("base is not a directory or it does not exists")
            return nil
        }           


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

        // Find number of common path components:
        let i = Set(destComponents).intersection(Set(baseComponents)).count

        // Build relative path:
        let relComponents = Array(repeating: "..", count: baseComponents.count - i) +
                            destComponents[i...]
        return relComponents.joined(separator: "/")
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48351839

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档