gpt4 book ai didi

macos - 如何确定两个文件路径(或文件 URL)是否标识 macOS 上的同一个文件或目录?

转载 作者:行者123 更新时间:2023-12-03 16:09:31 24 4
gpt4 key购买 nike

想象一下 macOS 上两个路径的简单示例:

  • /etc/hosts
  • /private/etc/hosts

  • 两者都指向同一个文件。但是你如何确定呢?
    另一个例子:
  • ~/Desktop
  • /Users/yourname/Desktop

  • 或者不区分大小写的文件系统上的大写/小写混合怎么样:
  • /Volumes/external/my file
  • /Volumes/External/My File

  • 甚至这个:
  • /Applications/Über.app

  • 此处:“Ü”可以指定为两个 unicode composition格式(NFD、NFC)。有关使用 (NS)URL API 时可能发生这种情况的示例,请参阅 this gist of mine .
    从 macOS 10.15 (Catalina) 开始,还有 firmlinks在卷组中从一个卷链接到另一个卷。同一个 FS 对象的路径可以写成:
  • /Applications/Find Any File.app
  • /System/Volumes/Data/Applications/Find Any File.app

  • 我喜欢记录可靠地处理所有这些复杂问题的方法,目标是高效(即快速)。

    最佳答案

    有两种方法可以检查两个路径(或它们的文件 URL)是否指向同一个文件系统项:

  • 比较他们的路径。这需要先准备好路径。
  • 比较它们的 ID( inode )。这总体上更安全,因为它避免了 unicode 错综复杂和错误大小写的所有并发症。

  • 比较文件 ID
    在 ObjC 中,这相当容易(注意:对于知识渊博的 Apple 开发人员来说,不应依赖 [NSURL fileReferenceURL],因此此代码使用了一种更简洁的方式):
    NSString *p1 = @"/etc/hosts";
    NSString *p2 = @"/private/etc/hosts";
    NSURL *url1 = [NSURL fileURLWithPath:p1];
    NSURL *url2 = [NSURL fileURLWithPath:p2];

    id ref1 = nil, ref2 = nil;
    [url1 getResourceValue:&ref1 forKey:NSURLFileResourceIdentifierKey error:nil];
    [url2 getResourceValue:&ref2 forKey:NSURLFileResourceIdentifierKey error:nil];

    BOOL equal = [ref1 isEqual:ref2];
    Swift 中的等价物(注意:不要使用 fileReferenceURL ,参见 this bug report ):
    let p1 = "/etc/hosts"
    let p2 = "/private/etc/hosts"
    let url1 = URL(fileURLWithPath: p1)
    let url2 = URL(fileURLWithPath: p2)

    let ref1 = try url1.resourceValues(forKeys[.fileResourceIdentifierKey])
    .fileResourceIdentifier
    let ref2 = try url2.resourceValues(forKeys[.fileResourceIdentifierKey])
    .fileResourceIdentifier
    let equal = ref1?.isEqual(ref2) ?? false
    两种解决方案都使用 BSD 功能 lstat在引擎盖下,所以你也可以用普通的 C 来写:
    static bool paths_are_equal (const char *p1, const char *p2) {
    struct stat stat1, stat2;
    int res1 = lstat (p1, &stat1);
    int res2 = lstat (p2, &stat2);
    return (res1 == 0 && res2 == 0) &&
    (stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino);
    }
    但是,请注意 warning关于使用这些类型的文件引用:

    The value of this identifier is not persistent across system restarts.


    这主要用于卷 ID,但也可能影响不支持持久文件 ID 的文件系统上的文件 ID。
    比较路径
    要比较路径,您必须 获取他们的规范路径 第一的。
    如果你不这样做,你就不能确定 案例是正确的,这反过来会导致非常复杂的比较代码。 (有关详细信息,请参阅 using NSURLCanonicalPathKey 。)
    案件的处理方式有多种:
  • 用户可能手动输入了名称,大小写错误。
  • 您之前存储了路径,但用户在此期间重命名了文件的大小写。您的路径仍将标识同一个文件,但现在情况是错误的,相等路径的比较可能会失败,具体取决于您如何获得与之比较的其他路径。

  • 只有当您从文件系统操作中获得路径时,您不能错误地指定路径的任何部分(即错误的情况),您才不需要获取规范路径,而只需调用 standardizingPath然后比较它们的路径是否相等(不需要不区分大小写的选项)。
    否则,为了安全起见,从这样的 URL 获取规范路径:
    import Foundation

    let uncleanPath = "/applications"
    let url = URL(fileURLWithPath: uncleanPath)

    if let resourceValues = try? url.resourceValues(forKeys: [.canonicalPathKey]),
    let resolvedPath = resourceValues.canonicalPath {
    print(resolvedPath) // gives "/Applications"
    }
    如果您的路径存储在字符串中而不是 URL 对象中,您可以调用 stringByStandardizingPath ( Apple Docs )。但这既不能解决不正确的大小写,也不能分解字符,这可能会导致问题,如 aforementioned gist 所示。 .
    因此,更安全的做法是从字符串创建文件 URL,然后使用上述方法获取规范路径,或者更好的是,使用 lstat() 解决方案来比较文件 ID,如上所示。
    还有一个 BSD 函数可以从 C 字符串中获取规范路径: realpath() .但是,这并不安全,因为它不会将卷组中不同路径的情况(如问题中所示)解析为相同的字符串。因此,为此应避免使用此功能。

    关于macos - 如何确定两个文件路径(或文件 URL)是否标识 macOS 上的同一个文件或目录?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66968422/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com