有两种方法可以检查两个路径(或其文件 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 关于使用这些文件引用的信息:
此标识符的值不会在系统重新启动后保持不变。
这主要用于卷 ID,但也可能影响不支持持久文件 ID 的文件系统上的文件 ID。
比较路径
要比较路径,您必须首先获取它们的规范路径。
如果不这样做,就无法确定case是否正确,进而导致比较代码非常复杂。 (详见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()。但是,这并不安全,因为它不会将卷组中不同路径的情况(如问题所示)解析为相同的字符串。因此,应避免使用此功能。