Как мне найти правильный регистр имени файла?

Это на Mac:

Если у меня два имени файла / foo / foo и / foo / FOO, они могут относиться к одному и тому же файлу или могут быть разными файлами в зависимости от файловой системы. Как выяснить, если они оба указывают на один и тот же файл? И если они есть, как я могу получить правильное представление имени файла?

Моя проблема вызвана ссылками. Ссылка может указывать на / foo / FOO, но фактический каталог называется / foo / foo.

Есть ли какая-нибудь функция, которая будет переходить по ссылке и давать мне полный путь к связанному файлу? [NSFileManager pathContentOfSymbolicLinkAtPath] дает относительные пути, которые могут быть в неправильном случае.

В конечном итоге я пытаюсь сделать кеш-информацию для файлов. Но если у меня есть два разных пути для одного и того же файла, мой кэш может выйти из синхронизации.

Спасибо

16.12.2008 00:35:28
Если два пути к файлу указывают на один и тот же индекс, ни один из них не имеет приоритета. Единого «правильного представления» не существует. Таким образом, / foo / FOO и / foo / foo одинаково законны. Изменение одного меняет другое. Удаление одного не влияет на другое. Файловое пространство не освобождается, пока не исчезнут все ссылки.
dkretz 16.12.2008 01:01:06
HFS + нечувствителен к регистру, но поддерживает регистр. Так что / foo / foo - это фактическое имя файла. fopen ("/ foo / FOO", "r") откроет тот же файл, но вы можете сказать, что это не "правильное" имя файла. Меня не волнует случай, когда есть две жесткие ссылки на один и тот же файл.
Roland Rabien 16.12.2008 02:07:31
5 ОТВЕТОВ
РЕШЕНИЕ

На ваш вопрос действительно есть пара разных частей. По моим прочтениям, вы хотите:

1 способ определить, являются ли два разных пути одним и тем же файлом на диске

2 каноническое имя файла на диске с соответствующим регистром

Есть и третья проблема, которая также связана с отображаемыми именами , потому что в OS X файл может локализовать свое имя и выглядеть по-разному для разных локалей. Итак, давайте добавим

3 способ получить отображаемое имя, потому что мы можем захотеть кэшировать вещи в зависимости от того, как пользователь видит файловую систему, а не от того, как файловая система отображается в терминале.

Мы можем решить 1 с помощью трюка FSRef, указанного @ boaz-stuller. Или вот некоторый код, который делает это, используя высокоуровневые вызовы Какао, что экономит нам немного жонглирования памяти (так как мы можем позволить 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] Сравнение файлов:
    / users / tal [inode # 1061692]
        на самом деле / ​​Пользователи / Тал
        и отображается как / Пользователи / Тал
    / USERS / TAL [индекс # 1061692]
        на самом деле / ​​Пользователи / Тал
        и отображается как / Пользователи / Тал
 Тот же файл.
15
16.12.2008 08:02:56
Вау, классный ответ! Я не ожидал, что ты так много сделаешь для меня. Бесконечно благодарен! getActualPath () это именно то, что мне нужно.
Roland Rabien 16.12.2008 12:50:28
У этого кода есть пара проблем. 1) Иноды являются уникальными для каждого устройства, поэтому вам действительно следует сравнить пары устройство / инод. 2) Фактический путь к malloc'у просочился. Чтобы исправить это, вы можете просто объявить его как UInt8 actualPath [PATH_MAX + 1].
Boaz Stuller 16.12.2008 16:36:36
Кроме того, я обычно нахожу FSRefs проще в использовании, чем пути. Пути имеют множество раздражающих крайних случаев (например, см. Этот вопрос :)), в то время как FSRefs в основном Just Work ™. Единственное большое ограничение FSRefs и путей - это то, что пути могут ссылаться на файлы, которые еще не существуют, в то время как FSRefs не могут.
Boaz Stuller 16.12.2008 16:42:31
Нет проблем. Хотя более простой метод (который до сих пор мне не приходил в голову), который работал бы до тех пор, пока эти ключи никогда не показываются пользователю, состоит в том, чтобы просто использовать строчные буквы всех ваших путей перед их использованием. Просто и эффективно!
TALlama 16.12.2008 17:21:06
За исключением регистрозависимых файловых систем. Пути трудны.
Boaz Stuller 16.12.2008 17:25:46

AFAIK, по умолчанию файловая система в Mac OS X нечувствительна к регистру, поэтому регистр ссылки или имени файла не должен иметь значения.

-2
16.12.2008 00:40:20
Мой новый iMac с Leopard предоставил мне выбор при первом включении. Таким образом, вполне возможно иметь установленный на заводе Mac с чувствительной к регистру файловой системой.
Rob Kennedy 16.12.2008 01:44:30
То, что пользователь получает по умолчанию, не имеет значения. Пользователь может иметь чувствительную к регистру файловую систему, HFSX или UFS, на любом устройстве. Любое приложение, которое не обрабатывает это правильно, не работает.
Peter Hosey 16.12.2008 02:28:03
Добавьте Photoshop CS3 в список «сломанных» приложений. Я все еще раздражен, мне пришлось переформатировать только для того, чтобы установить его, и если бы это было какое-то другое приложение, я бы просто удалил приложение вместо этого.
Marc Charbonneau 16.12.2008 04:23:47
Следствие моего предыдущего комментария: не основывайте свое поведение на загрузочной файловой системе. У пользователя могут быть установлены другие FS, которые находятся в других форматах с другим поведением случая. Вести себя соответственно для каждого пути.
Peter Hosey 16.12.2008 20:57:11

если os x является производной от Unix, есть ли у вас доступ к inode #?

2
16.12.2008 00:41:55
Это было бы лучшей идеей. Функция stat (2) должна возвращать полезный номер инода. Обратите внимание, что пара (device, inode) является уникальным ключом, поскольку разные устройства имеют свои собственные последовательности номеров inode.
Greg Hewgill 16.12.2008 00:44:45
Это должно работать для основной файловой системы. Я не знаю, будет ли это работать для смонтированного диска NTFS или FAT32.
Roland Rabien 16.12.2008 00:49:33
Тогда «правильное представление имени файла» не является разделителем. Файловые ссылки на индекс являются первоклассными именами файлов - первичного имени нет.
dkretz 16.12.2008 00:58:04

Используйте FSPathMakeRef()оба пути, а затем используйте, FSCompareFSRefs()чтобы увидеть, являются ли они одним файлом / папкой. Затем вы можете использовать , FSRefMakePath()чтобы получить каноническое представление, но если вы показываете имя файла для пользователя, вы должны использовать NSFileManager's- displayNameAtPath:метод вместо этого, так что локализация ручки и показа / скрытия расширений должным образом.

6
7.09.2012 08:43:45

Если кто-то пытается сделать это на iOS, FSRef недоступен. Единственный способ выяснить, как получить «фактическое» имя файла с правильным регистром, - перечислить содержимое родительского каталога, а затем сопоставить.

iOS - это чувствительная к регистру файловая система, а OS X (и симулятор) - нет. Я написал этот код, чтобы определить, когда существует заданный путь, но в симе неправильный регистр.

Если у кого-то есть лучшие способы сделать это (кроме этой уродливой петли), я весь в ушах.

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;
    }
}

}
0
1.09.2011 21:32:53