2011-08-25 10 views
10

名前の衝突を避けるために、特定のファイルパスを取得して変更する簡単な方法はありますか?型の指定されたパスのこと名前の衝突を避けるNSString固有のファイルパス

[StringUtils stringToAvoidNameCollisionForPath:path]; 

:ような何か/foo/bar/file.png/foo/bar/file-1.pngを返しますし、後でそれをインクリメントすることを「-1」同様にSafariがダウンロードされたファイルのために何をするかに。

UPDATE:

私はアッシュ畦の提案に従い、私は、ファイルに名前を付けるためにしようとしている、同様の問題があったが、わずかに広いアプローチを思い付いた答え:)

+3

? '[NSProcessInfo processInfo] globallyUniqueString]をチェックしてください。 – 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]; 
} 

...そして次は私が使用したテストです:ファイル名やフォルダ名としてGUIDを使用しない理由

- (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と同じ方法(ライブラリーを管理するように設定していて、同じ名前の複数のトラックがある場合など)

ループで動作するので、関数は複数回呼び出されても有効です出力。引数を説明すると、folderはパス(例: "/ foo/bar")であり、fileTypeは単なる拡張子(例: "png")にすぎません。fileNameはパスまたは拡張子のないファイル。これらの3つの文字列を1つの文字列として渡すことができ、後に分割することができますが、私の場合は、それらを分離することが理にかなっていました。

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

甘い。それは良いです。しかし、それは "名前3. 2"というよりも "名前1 2 3.ext"であることに気づいた。 –

+0

それは私にとっては意図的です。最後に数字の付いたファイル名を渡している場合は、それを保持したいものとみなします。元のファイル名の末尾にある数字と空白を削除するのは、簡単な操作であれば簡単です。 – Dov

関連する問題