Search code examples
objective-cfile-ionsstringenumeratensstream

Objective-C: Reading a file line by line


What is the appropriate way of dealing with large text files in Objective-C? Let's say I need to read each line separately and want to treat each line as an NSString. What is the most efficient way of doing this?

One solution is using the NSString method:

+ (id)stringWithContentsOfFile:(NSString *)path 
      encoding:(NSStringEncoding)enc 
      error:(NSError **)error 

and then split the lines with a newline separator, and then iterate over the elements in the array. However, this seems fairly inefficient. Is there no easy way to treat the file as a stream, enumerating over each line, instead of just reading it all in at once? Kinda like Java's java.io.BufferedReader.


Solution

  • That's a great question. I think @Diederik has a good answer, although it's unfortunate that Cocoa doesn't have a mechanism for exactly what you want to do.

    NSInputStream allows you to read chunks of N bytes (very similar to java.io.BufferedReader), but you have to convert it to an NSString on your own, then scan for newlines (or whatever other delimiter) and save any remaining characters for the next read, or read more characters if a newline hasn't been read yet. (NSFileHandle lets you read an NSData which you can then convert to an NSString, but it's essentially the same process.)

    Apple has a Stream Programming Guide that can help fill in the details, and this SO question may help as well if you're going to be dealing with uint8_t* buffers.

    If you're going to be reading strings like this frequently (especially in different parts of your program) it would be a good idea to encapsulate this behavior in a class that can handle the details for you, or even subclassing NSInputStream (it's designed to be subclassed) and adding methods that allow you to read exactly what you want.

    For the record, I think this would be a nice feature to add, and I'll be filing an enhancement request for something that makes this possible. :-)


    Edit: Turns out this request already exists. There's a Radar dating from 2006 for this (rdar://4742914 for Apple-internal people).