Tho*_*ann 5 filesystems macos nsurl nsfilemanager
想象一下 macOS 上两个路径的简单示例:
/etc/hosts
/private/etc/hosts
两者都指向同一个文件。但是你如何确定呢?
另一个例子:
~/Desktop
/Users/yourname/Desktop
或者不区分大小写的文件系统上的大写/小写混合怎么样:
/Volumes/external/my file
/Volumes/External/My File
甚至这个:
/Applications/Über.app
此处:“Ü”可以用两种unicode 组合格式(NFD、NFC)指定。有关使用 (NS)URL API 时可能发生这种情况的示例,请参阅我的这个要点。
从 macOS 10.15 (Catalina) 开始,还有另外的Firmlinks从一个卷链接到卷组中的另一个。同一个 FS 对象的路径可以写成:
/Applications/Find Any File.app
/System/Volumes/Data/Applications/Find Any File.app
我喜欢记录可靠地处理所有这些复杂问题的方法,目标是高效(即快速)。
有两种方法可以检查两个路径(或它们的文件 URL)是否指向同一个文件系统项:
在 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];
Run Code Online (Sandbox Code Playgroud)
Swift 中的等效项(注意:不要使用fileReferenceURL
,请参阅此错误报告):
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
Run Code Online (Sandbox Code Playgroud)
两种解决方案都在后台使用了 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);
}
Run Code Online (Sandbox Code Playgroud)
但是,请注意有关使用此类文件引用的警告:
此标识符的值在系统重新启动时不是持久的。
这主要用于卷 ID,但也可能影响不支持持久文件 ID 的文件系统上的文件 ID。
要比较路径,您必须首先获得它们的规范路径。
如果不这样做,就无法确定大小写是否正确,进而会导致非常复杂的比较代码。(有关详细信息,请参阅使用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"
}
Run Code Online (Sandbox Code Playgroud)
如果您的路径存储在 String 而不是 URL 对象中,您可以调用stringByStandardizingPath
( Apple Docs )。但这既不能解决不正确的大小写,也不能分解字符,这可能会导致上述要点所示的问题。
因此,从 String 创建文件 URL 然后使用上述方法获取规范路径更安全,或者更好的是,使用 lstat() 解决方案来比较文件 ID,如上所示。
还有一个 BSD 函数可以从 C 字符串中获取规范路径:realpath()
. 但是,这并不安全,因为它不会将卷组中不同路径的情况(如问题中所示)解析为相同的字符串。因此,为此应避免使用此功能。
归档时间: |
|
查看次数: |
269 次 |
最近记录: |