objective-ccocoansfilehandle

How to read data from NSFileHandle line by line?


I have a text file having data as given

e.g.

PUFGUjVRallYZDNaazFtVjVObU1zWm5ZcUJUYU5ORk4zbGthNHNDVUdSMlFVQmpSVEoxUUNSallYaFhkanBITXBGR1NTQnpZRTltZE1OalVzSkdXQ0Z6WXR0V2RpTmpTdXgwTWs5V1lZSkZiWjFXT29OV2JSVlhaSTUwYUpwR040UUZXTzVHVXFoWFVRcFdWNHdVTUJ0Q1VHSmxXVlJVTlJCMVE1VTFWV
PUFGUjVRallYZDNaazFtVjVObU1zWm5ZcUJUYU5ORk4zbGthNHNDVUdSMlFVQmpSVEoxUUNSallYaFhkanBITXBGR1NTQnpZRTltZE1OalVzSkdXQ0Z6WXR0V2RpTmpTdXgwTWs5V1lZSkZiWjFXT29OV2JSVlhaSTUwYUpwR040UUZXTzVHVXFoWFVRcFdWNHdVTUJ0Q1VHSmxXVlJVTlJCMVE1VTFWV

Now I want to read data line by line. That means first I want to read

PUFGUjVRallYZDNaazFtVjVObU1zWm5ZcUJUYU5ORk4zbGthNHNDVUdSMlFVQmpSVEoxUUNSallYaFhkanBITXBGR1NTQnpZRTltZE1OalVzSkdXQ0Z6WXR0V2RpTmpTdXgwTWs5V1lZSkZiWjFXT29OV2JSVlhaSTUwYUpwR040UUZXTzVHVXFoWFVRcFdWNHdVTUJ0Q1VHSmxXVlJVTlJCMVE1VTFWV

and then next remaining. anyone have any idea??


Solution

  • If your file is small, then @mipadi's method will probably be just fine. However, if your file is large (> 1MB, perhaps?), then you may want to consider reading the file line-by-line. I wrote a class once to do that, which I'll paste here:

    //DDFileReader.h
    
    @interface DDFileReader : NSObject {
        NSString * filePath;
        
        NSFileHandle * fileHandle;
        unsigned long long currentOffset;
        unsigned long long totalFileLength;
        
        NSString * lineDelimiter;
        NSUInteger chunkSize;
    }
    
    @property (nonatomic, copy) NSString * lineDelimiter;
    @property (nonatomic) NSUInteger chunkSize;
    
    - (id) initWithFilePath:(NSString *)aPath;
    
    - (NSString *) readLine;
    - (NSString *) readTrimmedLine;
    
    #if NS_BLOCKS_AVAILABLE
    - (void) enumerateLinesUsingBlock:(void(^)(NSString*, BOOL *))block;
    #endif
    
    @end
    
    
    //DDFileReader.m
    
    #import "DDFileReader.h"
    
    @interface NSData (DDAdditions)
    
    - (NSRange) rangeOfData_dd:(NSData *)dataToFind;
    
    @end
    
    @implementation NSData (DDAdditions)
    
    - (NSRange) rangeOfData_dd:(NSData *)dataToFind {
        
        const void * bytes = [self bytes];
        NSUInteger length = [self length];
        
        const void * searchBytes = [dataToFind bytes];
        NSUInteger searchLength = [dataToFind length];
        NSUInteger searchIndex = 0;
        
        NSRange foundRange = {NSNotFound, searchLength};
        for (NSUInteger index = 0; index < length; index++) {
            if (((char *)bytes)[index] == ((char *)searchBytes)[searchIndex]) {
                //the current character matches
                if (foundRange.location == NSNotFound) {
                    foundRange.location = index;
                }
                searchIndex++;
                if (searchIndex >= searchLength) { return foundRange; }
            } else {
                searchIndex = 0;
                foundRange.location = NSNotFound;
            }
        }
        return foundRange;
    }
    
    @end
    
    @implementation DDFileReader
    @synthesize lineDelimiter, chunkSize;
    
    - (id) initWithFilePath:(NSString *)aPath {
        if (self = [super init]) {
            fileHandle = [NSFileHandle fileHandleForReadingAtPath:aPath];
            if (fileHandle == nil) {
                [self release]; return nil;
            }
            
            lineDelimiter = [[NSString alloc] initWithString:@"\n"];
            [fileHandle retain];
            filePath = [aPath retain];
            currentOffset = 0ULL;
            chunkSize = 10;
            [fileHandle seekToEndOfFile];
            totalFileLength = [fileHandle offsetInFile];
            //we don't need to seek back, since readLine will do that.
        }
        return self;
    }
    
    - (void) dealloc {
        [fileHandle closeFile];
        [fileHandle release], fileHandle = nil;
        [filePath release], filePath = nil;
        [lineDelimiter release], lineDelimiter = nil;
        currentOffset = 0ULL;
        [super dealloc];
    }
    
    - (NSString *) readLine {
        if (currentOffset >= totalFileLength) { return nil; }
        
        NSData * newLineData = [lineDelimiter dataUsingEncoding:NSUTF8StringEncoding];
        [fileHandle seekToFileOffset:currentOffset];
        NSMutableData * currentData = [[NSMutableData alloc] init];
        BOOL shouldReadMore = YES;
        
        NSAutoreleasePool * readPool = [[NSAutoreleasePool alloc] init];
        while (shouldReadMore) {
            if (currentOffset >= totalFileLength) { break; }
            NSData * chunk = [fileHandle readDataOfLength:chunkSize];
            NSRange newLineRange = [chunk rangeOfData_dd:newLineData];
            if (newLineRange.location != NSNotFound) {
                
                //include the length so we can include the delimiter in the string
                chunk = [chunk subdataWithRange:NSMakeRange(0, newLineRange.location+[newLineData length])];
                shouldReadMore = NO;
            }
            [currentData appendData:chunk];
            currentOffset += [chunk length];
        }
        [readPool release];
        
        NSString * line = [[NSString alloc] initWithData:currentData encoding:NSUTF8StringEncoding];
        [currentData release];
        return [line autorelease];
    }
    
    - (NSString *) readTrimmedLine {
        return [[self readLine] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    }
    
    #if NS_BLOCKS_AVAILABLE
    - (void) enumerateLinesUsingBlock:(void(^)(NSString*, BOOL*))block {
      NSString * line = nil;
      BOOL stop = NO;
      while (stop == NO && (line = [self readLine])) {
        block(line, &stop);
      }
    }
    #endif
    
    @end
    

    Then to use this, you'd do:

    DDFileReader * reader = [[DDFileReader alloc] initWithFilePath:pathToMyFile];
    NSString * line = nil;
    while ((line = [reader readLine])) {
      NSLog(@"read line: %@", line);
    }
    [reader release];
    

    Or (for 10.6+ and iOS 4+):

    DDFileReader * reader = [[DDFileReader alloc] initWithFilePath:pathToMyFile];
    [reader enumerateLinesUsingBlock:^(NSString * line, BOOL * stop) {
      NSLog(@"read line: %@", line);
    }];
    [reader release];