views:

796

answers:

4

How to read only "N" bytes from a specified file?

A: 

Open the file:

NSData *fileData = [NSData dataWithContentsOfFile:fileName];

Read the bytes you want:

int bytes[1000];
[fileData getBytes:bytes length:sizeof(int) * 1000];
Alex
I don't know cocoa, but: why are you using `int` rather than `char`?
rascher
In this example the entire file has to be read before reading those N bytes. In my case when the file is approx 900 MB this method seems to be a bit to "hardcore"
Grzegorz Kazulak
An int is at least two bytes (usually 4), and originally I did write it to use char, which is generally one byte. I usually think it's best not to assume that any type has a specific number of bytes. That's probably attributable to my UNIX days :-)
Alex
There's also getBytes:range: method that takes a range to skip to the section of the file you're interested in.
Alex
@Alex: Can you give and example for my scenario?
Grzegorz Kazulak
Alex: That still reads the entire file, then excerpts it. You're paying both for memory and time to read the whole file and for time to copy the sub-range.
Peter Hosey
+1  A: 

If you want to avoid reading the entire file, you can just use the standard C I/O functions:

#include <stdio.h>
...
FILE *file = fopen("the-file.dat", "rb");
if(file == NULL)
    ; // handle error
char theBuffer[1000];  // make sure this is big enough!!
size_t bytesRead = fread(theBuffer, 1, 1000, file);
if(bytesRead < 1000)
    ; // handle error
fclose(file);
Adam Rosenfield
I wonder if this will correctly resolve Finder aliases in the path given?
neoneye
It should; I believe that Finder aliases are just symbolic links, and stdio can handle symlinks properly. To figure out the target of a symlink istead of reading the target file, one can use the readlink(2) function, see http://linux.die.net/man/2/readlink .
Adam Rosenfield
Finder aliases are not symbolic links. Finder supports both, but creates aliases.
Peter Hosey
Ah ok, never mind then. This will not work with Finder aliases then.
Adam Rosenfield
+7  A: 

-[NSFileHandle readDataOfLength:].

Peter Hosey
Can you give an example please?
Grzegorz Kazulak
[[NSFileHandle fileHandleForReadingAtPath:path] readDataOfLength:length]
Peter Hosey
+5  A: 

If you want random access to the contents of the file in a manner similar to having loaded it via NSData but without actually reading everything into memory, you can use memory mapping. Doing so means that the file on disk becomes treated as a section of virtual memory, and will be paged in and out just like regular virtual memory.

NSError * error = nil;
NSData * theData = [NSData dataWithContentsOfFile: thePath
                                          options: NSMappedRead
                                            error: &error];

If you don't care about getting filesystem error details, you can just use:

NSData * theData = [NSData dataWithContentsOfMappedFile: thePath];

Then you would just use NSData's -getBytes:range: method to pull out specific pieces of data, and only the relevant parts of the file will actually be read from permanent storage; they'll also be eligible to be paged out too.

Jim Dovey
That's a cool idea, I wouldn't have thought of that. Remember that when mapping files to memory, it's arguably even more critical to make sure you properly release the object, since leaks have a broader effect.
Quinn Taylor