Я решил реализовать свое собственное решение и хочу поделиться своим кодом. Это не самая желательная реализация, но, похоже, она выполняет свою работу:
+ (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);
}