2011-08-25 81 views
10

是否有一種簡單的方法來獲取給定的文件路徑並修改它以避免名稱衝突?喜歡的東西:NSString獨特的文件路徑,以避免名稱衝突

[StringUtils stringToAvoidNameCollisionForPath:path]; 

,對於類型的給定路徑:/foo/bar/file.png,將返回/foo/bar/file-1.png後來人們都會增加「-1」類似於什麼Safari瀏覽器也對下載的文件。

UPDATE:

我跟着灰溝的建議和我貼我的實現作爲答案:)

+3

爲什麼不使用GUID作爲文件名或文件夾名稱?檢查''[[NSProcessInfo processInfo] globalUniqueString]' – Eimantas

+0

不,我的問題是沒有關係使一個字符串唯一(我會在這種情況下使用CFUUIDCreate()),但我想保留文件名並只添加一個後綴它是獨一無二的:P – daveoncode

+0

我實現了我自己的解決方案......歡迎評論:) – daveoncode

回答

1

我決定實施我自己的解決方案,我想分享我的代碼。這不是最可取的實施,但似乎做的工作:

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

} 
3

我也有類似的問題,並用稍寬的方法上來,試圖命名文件iTunes會採用同樣的方式(當你設置爲管理你的音樂庫,並且你有多個同名的音軌等)

它工作在一個循環中,所以這個函數可以多次調用並且仍然產生有效的輸出。解釋參數,fileName是沒有路徑或擴展名(例如「文件」)的文件的名稱,folder只是路徑(例如「/ foo/bar」),而fileType只是擴展名(例如「png」) 。這三個可以作爲一個字符串傳遞並在之後分開,但在我的情況下,將它們分開是有意義的。

currentPath(它可以是空的,但不爲零)在重命名文件而不是創建新文件時非常有用。例如,如果您嘗試重命名爲「/foo/bar/file.png」的「/ foo/bar/file 1.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; 
} 
+0

甜。這很好。但注意到它的名稱是「name 1 2 3.ext」,而不是「name 3.ext」,但是不管怎樣。 –

+0

這是我的意圖。如果你最後傳入一個帶有數字的文件名,這就是你想要保留的東西。修改原始文件名末尾的數字和空格會很容易,如果這不是所期望的行爲。 – Dov