2011-08-25 6 views
10

Ad çarpışmalarını önlemek için belirli bir dosya yolunu almanın ve değiştirmenin basit bir yolu var mı? tip belirli bir yolu için buNSString isim çarpışmasını önlemek için benzersiz dosya yolu

[StringUtils stringToAvoidNameCollisionForPath:path]; 

: Böyle bir şey /foo/bar/file.png, /foo/bar/file-1.png dönecektir ve daha sonra artırır edeceği "-1" benzer Safari indirilen dosyalar için ne kadar.

GÜNCELLEME:

Ben Kül Karık önerisini takip ettim ve ben benzer bir sorun vardı

+3

neden olmasın dosya veya klasör adı olarak GUID kullanılır? '[[NSProcessInfo processInfo] globallyUniqueString]' i kontrol edin – Eimantas

+0

Hayır, sorunum bir dize benzersiz yapmakla ilgili değil (bu durumda CFUUIDCreate() kullanacağım), ancak dosya adını korumak ve yalnızca bir sonek eklemek istiyorum. Bu benzersiz: P – daveoncode

+0

Kendi çözümüme başvurdum ... yorum açıktır :) – daveoncode

cevap

1

Kendi çözümümü uygulamaya karar verdim ve kodumu paylaşmak istiyorum. Bu en çok arzu edilen uygulama değil, ama işi yapmak için görünüyor:

+ (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]; 
} 

... ve şu kullandığım testler şunlardır:

- (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); 

} 
3

:) cevap olarak benim uygulanmasını yayınlanmıştır ve biraz daha geniş bir yaklaşımla geldi, o dosyaları adlandırmak için çalışır Aynı şekilde iTunes (kitaplığınızı yönetmek için ayarladıysanız ve aynı ada sahip birden çok iziniz varsa, vb.)

Bir döngüde çalışır, böylece işlev birden çok kez çağrılabilir ve yine de geçerli olarak üretilebilir çıktı. argümanları açıklayan fileName hiçbir yol veya uzantılı dosyanın adı (örneğin, "dosya"), folder sadece yoludur (örneğin "/ foo/bar") ve fileType uzatma (örneğin, "png") adildir . Bu üçü tek bir ip gibi geçirebilir ve sonra ayrılabilir, ama benim durumumda onları ayırmak anlamlı oldu. Eğer bir yenisini oluşturmak değil bir dosya yeniden adlandırma yaparken

currentPath (boş, ancak sıfır olmayan olabilir), yararlıdır. Örneğin, "/foo/bar/file.png" olarak yeniden adlandırmaya çalıştığınız "/ foo/bar/file 1.png" varsa, "/ foo/bar/file 1.png" ifadesini geçirirsiniz. "currentPath için ve" /foo/bar/file.png "zaten varsa,"/foo/bar/file 1.png "ifadesini görmek yerine"/foo/bar/file 1.png "yazıp"/foo "ifadesini döndürmek yerine, başladığınız yolu geri alırsınız./bar/dosya 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; 
} 
+0

Sweet. Bu iyi. Ancak, "name 3.ext" yerine "name 1 2 3.ext" yazdığını, ancak her neyse olduğunu fark ettim. –

+0

Bu benim için kasıtlı. Sonunda bir sayıya sahip bir dosya adıyla geçiyorsanız, bu, tutmak istediğiniz bir şey olarak kabul edilir.İstenilen davranış değilse, sayıları ve boşlukları orijinal dosya adının sonuna kadar kırpmak kolay olacaktır. – Dov