如何找到文件名的正确大小写?

时间:2008-12-16 00:35:28

标签: objective-c cocoa macos

这是在Mac上:

如果我有两个文件名/ foo / foo和/ foo / FOO,它们可能引用相同的文件,或者可能是不同的文件,具体取决于文件系统。我怎么弄清楚它们是否都指向同一个文件?如果是,我如何得到正确的文件名表示?

我的问题是由链接引起的。链接可能指向/ foo / FOO,但实际目录名为/ foo / foo。

是否有任何功能会跟随链接并给我链接文件的完整路径? [NSFileManager pathContentOfSymbolicLinkAtPath]给出了可能出现错误情况的相对路径。

最终,我尝试做的是缓存文件的信息。但是如果我有两个不同的路径用于同一个文件,我的缓存可能会失去同步。

由于

5 个答案:

答案 0 :(得分:15)

你的问题确实有几个不同的部分。通过我的阅读,你想要:

  

1 一种判断两个不同路径是否相同的磁盘文件的方法

     

2 磁盘上文件的规范名称,带有正确的大小

还有第三个问题与Display Names有关,因为在OS X中,文件可以本地化其名称,并且对于不同的语言环境显示不同。所以让我们添加

  

3 获取显示名称的方法,因为我们可能希望根据用户查看文件系统的方式来缓存内容,而不是文件系统在终端中的显示方式。

我们可以用@ boaz-stuller指出的FSRef技巧解决 1 。或者这里有一些代码使用更高级别的Cocoa调用来完成它,这为我们节省了一些内存(因为我们可以让NSAutoreleasePool为我们做这些):

long getInode(NSString* path) {
    NSFileManager* fm = [NSFileManager defaultManager];
    NSError* error;
    NSDictionary* info = [fm attributesOfItemAtPath:path error:&error];
    NSNumber* inode = [info objectForKey:NSFileSystemFileNumber];
    return [inode longValue];
}

但是要解决 2 ,我们必须使用FSRefs找出该文件的规范框:

NSString* getActualPath(NSString* path) {
    FSRef ref;
    OSStatus sts;
    UInt8* actualPath;

    //first get an FSRef for the path
    sts = FSPathMakeRef((const UInt8 *)[path UTF8String], &ref, NULL);
    if (sts) return [NSString stringWithFormat:@"Error #%d making ref.", sts];

    //then get a path from the FSRef
    actualPath = malloc(sizeof(UInt8)*MAX_PATH_LENGTH);
    sts = FSRefMakePath(&ref, actualPath, MAX_PATH_LENGTH);
    if (sts) return [NSString stringWithFormat:@"Error #%d making path.", sts];

    return [NSString stringWithUTF8String:(const char*)actualPath];
}

这一点都不错,但是当我们用Cocoa方法解决 3 时我们仍然很高兴:

NSString* getDisplayPath(NSString* path) {
    NSFileManager* fm = [NSFileManager defaultManager];
    NSString* mine = [fm displayNameAtPath:path];
    NSString* parentPath = [path stringByDeletingLastPathComponent];
    NSString* parents = [@"/" isEqualToString:parentPath]
        ? @""
        : getDisplayPath(parentPath);
    return [NSString stringWithFormat:@"%@/%@", parents, mine];
}

最后,我们可以添加一些驱动程序代码并将它们整合到一个CoreFoundation命令行工具中(我必须添加AppKit框架才能进行编译)。

NSString* fileInfoString(NSString* path) {
    long inode = getInode(path);
    return [NSString stringWithFormat:
        @"\t%@  [inode #%d]\n\t\tis actually %@\n\t\tand displays as %@",
        path,
        inode,
        getActualPath(path),
        getDisplayPath(path)];
}

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    if (argc < 2) {
        NSLog(@"Usage: %s <path1> [<path2>]", argv[0]);
        return -1;
    }

    NSString* path1 = [NSString stringWithCString:argv[1]];
    NSString* path2 = argc > 2
        ? [NSString stringWithCString:argv[1]]
        : [path1 uppercaseString];

    long inode1 = getInode(path1);
    long inode2 = getInode(path2);

    NSString* prefix = [NSString stringWithFormat:
        @"Comparing Files:\n%@\n%@", 
        fileInfoString(path1), 
        fileInfoString(path2)];

    int retval = 0;
    if (inode1 == inode2) {
        NSLog(@"%@\nSame file.", prefix);
    } else {
        NSLog(@"%@\nDifferent files.", prefix);
        retval = 1;
    }

    [pool drain];
    return retval;
}

现在,我们可以将它们放在一起并运行它:

 $ checkpath /users/tal
 2008-12-15 23:59:10.605 checkpath[22375:10b] Comparing Files:
    /users/tal  [inode #1061692]
        is actually /Users/tal
        and displays as /Users/tal
    /USERS/TAL  [inode #1061692]
        is actually /Users/tal
        and displays as /Users/tal
 Same file.

答案 1 :(得分:6)

在两个路径上使用FSPathMakeRef(),然后使用FSCompareFSRefs()查看它们是否是同一个文件/文件夹。然后,您可以使用FSRefMakePath()来获取规范表示,但如果您向用户显示文件名,则应使用NSFileManager's - displayNameAtPath:方法,因为它处理本地化和显示/正确隐藏扩展名。

答案 2 :(得分:2)

如果os x是unix派生词,你有权访问inode#?

答案 3 :(得分:0)

如果有人试图在iOS上执行此操作,则FSRef不可用。我能弄清楚如何获得具有正确大小写的“实际”文件名的唯一方法是列出父目录的内容,然后匹配。

iOS是区分大小写的文件系统,而OS X(和模拟器)则不是。我编写了这段代码来检测给定路径何时存在,但在sim中有错误的情况。

如果有人有更好的方法来做到这一点(除了这个丑陋的循环)我都是耳朵。

void checkForCapsIssues(NSString* compiledPath)
{

NSArray* validFilePaths = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[[compiledPath stringByDeletingLastPathComponent] stringByResolvingSymlinksInPath] error:nil];
NSString* lastPathComponent = [compiledPath lastPathComponent];
for (NSString* fileName in validFilePaths) {
    if([fileName isEqualToString:lastPathComponent])
    {
        return;
    }
    if([[fileName lowercaseString] isEqualToString:[lastPathComponent lowercaseString]])
    {
        NSLog(@"Warning! Caps Problem Found! %@", compiledPath);
        return;
    }
}

}

答案 4 :(得分:-2)

AFAIK,默认情况下Mac OS X中的文件系统是Case Insensitive,因此链接或文件名的大小写无关紧要。