【问题标题】:How do I see if some folder is a child of another using NSURL?如何使用 NSURL 查看某个文件夹是否是另一个文件夹的子文件夹?
【发布时间】:2017-08-28 20:37:25
【问题描述】:

我有一个[URL],它代表一组特殊的父目录。我得到另一个[URL],它代表分散在系统各处的文件。我想知道这些文件中的任何一个是否在我的任何特殊目录或其任何子目录中。 是否有一种简单/有意的方法可以做到这一点,而无需手动解析/遍历绝对 URL 的路径?

【问题讨论】:

    标签: swift cocoa nsurl


    【解决方案1】:

    NSURL 中没有任何方法可以让您查看另一个 NSURL 是否代表另一个的根路径。

    一种可能的解决方案是使用path 属性将两个URL 转换为路径字符串。然后查看一个字符串是否是另一个字符串的前缀。但在获取 URL 的路径之前,请同时使用 URLByStandardizingPathURLByResolvingSymlinksInPath 以确保结果一致。

    例子:

    NSURL *specialURL = ... // a URL for one of the special parent directories
    NSURL *fileURL = ... // a URL for one of the files to check
    NSString *specialPath = [specialURL.URLByStandardizingPath.URLByResolvingSymlinksInPath.path stringByAppendingString:@"/"];
    NSString *filePath = fileURL.URLByStandardizingPath.URLByResolvingSymlinksInPath.path
    if ([filePath hasPrefix:specialPath]) {
        // This file is in this special directory
    }
    

    【讨论】:

    • 您需要在前缀测试之前向specialPath 添加一个斜杠,否则/super 之类的内容将被视为/supermarket/qfc 的父级。 (NSURLs path 属性去除任何尾随 /。)
    • @CRD 好点,谢谢。更新。奇怪的是,当您打印代表文件夹的文件路径的NSURL 时,会显示尾部斜杠。但是path 的结果不包括斜线。
    【解决方案2】:

    略有改进的 Swift 5 版本结合了 Umair 和 rmaddy 现有优秀答案的优秀特性。

    就像 Umair 的回答一样,这可以正确处理路径中的文件夹是部分匹配的情况(也就是说,它不会说 /foo/bar-baz/bang/foo/bar/ 作为祖先)。

    它还执行规范化(标准化路径以消除 .../ 之类的内容,并解析符号链接),如 rmaddy 的回答。

    func pathHasAncestor(maybeChild: URL, maybeAncestor: URL) -> Bool {
        let ancestorComponents: [String] = canonicalize(maybeAncestor).pathComponents
        let childComponents: [String] = canonicalize(maybeChild).pathComponents
    
        return ancestorComponents.count < childComponents.count
            && !zip(ancestorComponents, childComponents).contains(where: !=)
    }
    
    func canonicalize(_ url: URL) -> URL {
        url.standardizedFileURL.resolvingSymlinksInPath()
    }
    

    这是一个非常简单的单元测试来验证基本功能:

    import XCTest
    
    class FileUtilsTests: XCTestCase {
        func testPathHasAncestor() throws {
            let leaf = URL(fileURLWithPath: "/foo/bar/baz")
            let parent = leaf.deletingLastPathComponent()
            XCTAssertTrue(pathHasAncestor(maybeChild: leaf, maybeAncestor: parent))
            XCTAssertFalse(pathHasAncestor(maybeChild: URL(fileURLWithPath: "/foo/bar 1/baz"), maybeAncestor: parent))
            XCTAssertFalse(pathHasAncestor(maybeChild: leaf, maybeAncestor: leaf))
        }
    }
    

    【讨论】:

    • 这很好!它采用Umair's 背后的理念,使其更加可靠!我想我会在URL 上将其用作extension
    【解决方案3】:

    前缀解决方案的问题是它会在以下情况下失败:例如。

    PathA: /Documents/untitled folder (1)/SomeFolder/Xyz/Waterfall.mp3
    PathB: /Documents/untitled folder
    

    前缀解决方案会告诉您 PathA 是 PathB 的子节点,但事实并非如此。

    手动遍历解决方案:

    斯威夫特:

    extension String {
        func isChildPath(of path: String) -> Bool {
            self.count < path.count
                && !zip(URL(fileURLWithPath: self).pathComponents,
                        URL(fileURLWithPath: path).pathComponents)
                    .contains(where: !=)
        }
    }
    

    目标-C:

    @interface NSString (Additions)
    -(BOOL)isChildOfPath:(NSString *)path;
    @end
    @implementation NSString (Additions)
    -(BOOL)isChildOfPath:(NSString *)path {
       NSString * child = self;
       NSString * parent = path;
       NSArray<NSString *> * childPathComponents = child.pathComponents;
       NSArray<NSString *> * parentPathComponents = parent.pathComponents;
       if (parentPathComponents.count >= childPathComponents.count) {
            return NO;
       }
    
       for (NSInteger i = 0; i < parentPathComponents.count; i++) {
           NSString * parentComponent = parentPathComponents[i];
           NSString * childComponent = childPathComponents[i];
           if ( ![parentComponent isEqualToString:childComponent] ) {
               return NO;
           }
       }
       return YES;
    }
    @end
    

    【讨论】:

    • 感谢您的回答!它合法地指出了一个非常重要的问题!如果可以的话,我查看了您的 Swift 代码,它与这个单行版本相同:self.count &lt; path.count &amp;&amp; !zip((self as NSString).pathComponents, (path as NSString).pathComponents).contains(where: !=) - 如果我用它更新您的答案,您可以吗?此外,使用它们创建一个新 URL(使用 URL(fileURLWithPath:))而不是将它们桥接到 NSString 具有相同的成本 O(1),但适用于 Ubuntu。
    • 当然。您可以编辑和添加这个很棒的更简单的版本。
    猜你喜欢
    • 1970-01-01
    • 2012-06-03
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-28
    • 1970-01-01
    • 2019-11-24
    相关资源
    最近更新 更多