Swift相当于Ruby" Pathname.relative_path_from"

时间:2018-01-20 01:46:06

标签: swift url nsurl nsfilemanager server-side-swift

Ruby的Pathname.relative_path_from文档。

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

我更喜欢使用file://网址的解决方案,但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     |

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

3 个答案:

答案 0 :(得分:6)

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

以下是作为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: "/")
    }
}

我的测试代码:

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"

说明:

  • &#34;&#34;和&#34; ..&#34;删除给定的URL和相关文件URL 是绝对的(都使用standardized的{​​{1}}方法。)
  • 案例(in)敏感度处理。
  • 假设基本URL表示目录。

附录: @neoneye将其包装成Swift包: SwiftyRelativePath

答案 1 :(得分:1)

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

    connection.query("SELECT SUM(g_speicherplatz) AS g_speicherplatz, SUM(g_spielzeit) AS g_spielzeit, SUM(g_kosten) AS g_kosten FROM games", function(error, result, fields) {

我的测试用例有所扩展。情况4是我促成这一小变化的原因。

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.pathComponents
    let baseComponents = workBase.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: "/")
}

答案 2 :(得分:0)

我使用的是Kneup向导的版本,但是当基本目录具有扩展名时遇到问题。所以我添加代码来检查path是否存在并且是目录。

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: "/")
    }
}