NSString уникальный путь к файлу, чтобы избежать конфликтов имен - PullRequest
10 голосов
/ 25 августа 2011

Есть ли простой способ взять заданный путь к файлу и изменить его, чтобы избежать конфликтов имен?Что-то вроде:

[StringUtils stringToAvoidNameCollisionForPath:path];

, которое для заданного пути типа: /foo/bar/file.png вернет /foo/bar/file-1.png, а затем будет увеличивать это значение «-1» аналогично тому, что Safari делает для загруженных файлов.

ОБНОВЛЕНИЕ:

Я последовал предложению Эша Ферроу и опубликовал свою реализацию в качестве ответа:)

Ответы [ 2 ]

3 голосов
/ 28 ноября 2011

У меня была похожая проблема, и я предложил немного более широкий подход, который пытается именовать файлы так же, как iTunes (когда у вас настроено управление вашей библиотекой и у вас есть несколько дорожек с одинаковым именем и т. Д.).)

Работает в цикле, поэтому функцию можно вызывать несколько раз и при этом выводить действительный вывод.Объясняя аргументы, fileName - это имя файла без пути или расширения (например, «file»), folder - это просто путь (например, «/ foo / bar»), а fileType - просто расширение.(например, "PNG").Эти три можно передать как одну строку и разделить после, но в моем случае имеет смысл разделить их.

currentPath (который может быть пустым, но не ноль), полезно, когда выпереименовываешь файл, а не создаешь новый.Например, если у вас есть «/ foo / bar / file 1.png», который вы пытаетесь переименовать в «/foo/bar/file.png», вы должны передать «/ foo / bar / file 1.png».«для currentPath, а если« /foo/bar/file.png »уже существует, вы вернетесь к тому пути, с которого начали, вместо того, чтобы увидеть« / foo / bar / file 1.png »и вернуться»/ foo / bar / file 2.png "

+ (NSString *)uniqueFile:(NSString *)fileName
                inFolder:(NSString *)folder
           withExtension:(NSString *)fileType
        mayDuplicatePath:(NSString *)currentPath
{
    NSUInteger existingCount = 0;
    NSString *result;
    NSFileManager *manager = [NSFileManager defaultManager];

    do {
        NSString *format = existingCount > 0 ? @"%@ %lu" : @"%@";

        fileName = [NSString stringWithFormat:format, fileName, existingCount++];
        result = [fileName stringByAppendingFormat:@".%@", [fileType lowercaseString]];

        result = [folder stringByAppendingPathComponent:result];
    } while ([manager fileExistsAtPath:result] &&
             // This comparison must be case insensitive, as the file system is most likely so
             [result caseInsensitiveCompare:currentPath] != NSOrderedSame);

    return result;
}
1 голос
/ 20 декабря 2011

Я решил реализовать свое собственное решение и хочу поделиться своим кодом. Это не самая желательная реализация, но, похоже, она выполняет свою работу:

+ (NSString *)stringToAvoidNameCollisionForPath:(NSString *)path {

    // raise an exception for invalid paths
    if (path == nil || [path length] == 0) {
        [NSException raise:@"DMStringUtilsException" format:@"Invalid path"];
    }

    NSFileManager *manager = [[[NSFileManager alloc] init] autorelease];
    BOOL isDirectory;

    // file does not exist, so the path doesn't need to change
    if (![manager fileExistsAtPath:path isDirectory:&isDirectory]) {
        return path;
    }

    NSString *lastComponent = [path lastPathComponent];
    NSString *fileName = isDirectory ? lastComponent : [lastComponent stringByDeletingPathExtension];
    NSString *ext = isDirectory ? @"" : [NSString stringWithFormat:@".%@", [path pathExtension]];
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"-([0-9]{1,})$" options:0 error:nil];
    NSArray *matches = [regex matchesInString:fileName options:0 range:STRING_RANGE(fileName)];

    // missing suffix... start from 1 (foo-1.ext) 
    if ([matches count] == 0) {
        return [NSString stringWithFormat:@"%@-1%@", fileName, ext];
    }

    // get last match (theoretically the only one due to "$" in the regex)
    NSTextCheckingResult *result = (NSTextCheckingResult *)[matches lastObject];

    // extract suffix value
    NSUInteger counterValue = [[fileName substringWithRange:[result rangeAtIndex:1]] integerValue];

    // remove old suffix from the string
    NSString *fileNameNoSuffix = [fileName stringByReplacingCharactersInRange:[result rangeAtIndex:0] withString:@""];

    // return the path with the incremented counter suffix
    return [NSString stringWithFormat:@"%@-%i%@", fileNameNoSuffix, counterValue + 1, ext];
}

... и следующие тесты, которые я использовал:

- (void)testStringToAvoidNameCollisionForPath {

    NSBundle *bundle = [NSBundle bundleForClass:[self class]];  

    // bad configs //

    STAssertThrows([DMStringUtils stringToAvoidNameCollisionForPath:nil], nil);
    STAssertThrows([DMStringUtils stringToAvoidNameCollisionForPath:@""], nil);

    // files //

    NSString *path = [bundle pathForResource:@"bar-0.abc" ofType:@"txt"];
    NSString *savePath = [DMStringUtils stringToAvoidNameCollisionForPath:path];
    STAssertEqualObjects([savePath lastPathComponent], @"bar-0.abc-1.txt", nil);

    NSString *path1 = [bundle pathForResource:@"bar1" ofType:@"txt"];
    NSString *savePath1 = [DMStringUtils stringToAvoidNameCollisionForPath:path1];
    STAssertEqualObjects([savePath1 lastPathComponent], @"bar1-1.txt", nil);

    NSString *path2 = [bundle pathForResource:@"bar51.foo.yeah1" ofType:@"txt"];
    NSString *savePath2 = [DMStringUtils stringToAvoidNameCollisionForPath:path2];
    STAssertEqualObjects([savePath2 lastPathComponent], @"bar51.foo.yeah1-1.txt", nil);

    NSString *path3 = [path1 stringByDeletingLastPathComponent];
    NSString *savePath3 = [DMStringUtils stringToAvoidNameCollisionForPath:[path3 stringByAppendingPathComponent:@"xxx.zip"]];
    STAssertEqualObjects([savePath3 lastPathComponent], @"xxx.zip", nil);

    NSString *path4 = [bundle pathForResource:@"foo.bar1-1-2-3-4" ofType:@"txt"];
    NSString *savePath4 = [DMStringUtils stringToAvoidNameCollisionForPath:path4];
    STAssertEqualObjects([savePath4 lastPathComponent], @"foo.bar1-1-2-3-5.txt", nil);

    NSString *path5 = [bundle pathForResource:@"bar1-1" ofType:@"txt"];
    NSString *savePath5 = [DMStringUtils stringToAvoidNameCollisionForPath:path5];
    STAssertEqualObjects([savePath5 lastPathComponent], @"bar1-2.txt", nil);

    // folders //

    NSString *path6 = [DOCUMENTS_PATH stringByAppendingPathComponent:@"foo1"];
    NSString *savePath6 = [DMStringUtils stringToAvoidNameCollisionForPath:path6];
    STAssertEqualObjects([savePath6 lastPathComponent], @"foo1-1", nil);

    NSString *path7 = [DOCUMENTS_PATH stringByAppendingPathComponent:@"bar1-1"];
    NSString *savePath7 = [DMStringUtils stringToAvoidNameCollisionForPath:path7];
    STAssertEqualObjects([savePath7 lastPathComponent], @"bar1-2", nil);

    NSString *path8 = [DOCUMENTS_PATH stringByAppendingPathComponent:@"foo-5.bar123"];
    NSString *savePath8 = [DMStringUtils stringToAvoidNameCollisionForPath:path8];
    STAssertEqualObjects([savePath8 lastPathComponent], @"foo-5.bar123-1", nil);

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...