代码之家  ›  专栏  ›  技术社区  ›  Roland Rabien

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

  •  7
  • Roland Rabien  · 技术社区  · 17 年前

    这是在Mac上:

    如果我有两个文件名/foo/foo和/foo/foo,它们可能引用同一个文件,也可能是不同的文件,具体取决于文件系统。我如何判断它们是否都指向同一个文件?如果是,我如何获得文件名的正确表示?

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

    是否有任何函数可以跟随链接并向我提供链接文件的完整路径?[NSFileManager pathContentOfSymbolicLinkAtPath]给出的相对路径可能大小写不正确。

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

    谢谢

    5 回复  |  直到 17 年前
        1
  •  15
  •   Community Mohan Dere    5 年前

    你的问题实际上有几个不同的部分。根据我的阅读,你想要:

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

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

    还有第三个问题也混杂在一起,与 Display Names ,因为在OS X中,文件可以本地化其名称,并在不同的语言环境中显示不同的内容。那么,让我们添加

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

    我们可以解决 1. @boaz stuller指出了FSRef的伎俩。或者这里有一些代码,它使用更高级的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];
    }
    

    这一点也不坏,但当我们能够解决时,我们仍然很高兴 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.
    
        2
  •  6
  •   Parag Bafna    13 年前

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

        3
  •  2
  •   dkretz    17 年前

    如果osx是unix的衍生版本,您有权访问inode吗?

        4
  •  0
  •   Dimitri    14 年前

    如果有人试图在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;
        }
    }
    
    }
    
        5
  •  -2
  •   Grant Limberg    17 年前

    AFAIK,默认情况下,Mac OS X中的文件系统不区分大小写,因此链接或文件名的大小写应该无关紧要。