5

How do you efficiently check if two files are the same (have the same data) in Cocoa?

Context: I'm writing a program that receives a file as input (input file) and copies it into a directory. If the directory already contains a file with the same name (namesake file) then the input file should be copied with a new name only if the namesake file is different.

hpique
  • 119,096
  • 131
  • 338
  • 476

2 Answers2

6

you can use -[NSFileManager contentsEqualAtPath:andPath:].

From the Docs:

If path1 and path2 are directories, the contents are the list of files and subdirectories each contains—contents of subdirectories are also compared. For files, this method checks to see if they’re the same file, then compares their size, and finally compares their contents. This method does not traverse symbolic links, but compares the links themselves.

justin
  • 104,054
  • 14
  • 179
  • 226
  • Thanks! Do you know if there is any `contentsEqualAtPath:` equivalent for `NSFileWrapper`? – hpique Aug 28 '12 at 20:58
  • @hpique you're welcome. none that i know of -- you would need to remember or reconstruct the path/url from the source file wrapper (file, directory) you initialized it from. an `NSFileWrapper` does not need to mirror a physical location on the filesystem (obvious case: it could reside in memory). so just figure out a way to remember the location on disk. if your 'namesake file''s contents have already been loaded (e.g. by your `NSFileWrapper`), the process may be faster than you anticipate because the file is probably still in the cache. of course, it wouldn't be hard to write yourself, either – justin Aug 28 '12 at 21:43
  • but try just keeping track of the url first, then compare using `-[NSFileManager contentsEqualAtPath:andPath:]`. that would be adequate for many applications. – justin Aug 28 '12 at 21:45
  • 1
    Thanks again! I did exactly that. – hpique Aug 29 '12 at 08:14
3

While Justin answered my question, I was using NSFileWrapper internally so I couldn't always use contentsEqualAtPath:andPath:.

In case it helps anyone, here's what I wrote to compare the contents of a NSFileWrapper to the contents of a file:

- (BOOL) contentsOfFileWrapper:(NSFileWrapper*)fileWrapper equalContentsAtPath:(NSString*)path {        
    NSDictionary *fileAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
    NSUInteger fileSize = [attrs fileSize];
    NSUInteger fileWrapperSize = [fileWrapper.fileAttributes fileSize];  // Will return zero if the file wrapper hasn't been written
    if (fileWrapperSize > 0 && fileSize != fileWrapperSize) return NO;

    NSData *fileData = [NSData dataWithContentsOfURL:fileURL];
    NSData *fileWrapperData = fileWrapper.regularFileContents;
    return [fileData isEqualToData:resourceData];
}

As Justin suggested, I'm only using the above method if I'm unable to reconstruct the path of the file wrapper. If I can, then I use contentsEqualAtPath:andPath:.

hpique
  • 119,096
  • 131
  • 338
  • 476