Created
February 16, 2019 16:26
-
-
Save iamluisg/268699843041b007e07f9a33be35d62f to your computer and use it in GitHub Desktop.
[Read file line by line] #file #textImport
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Dave de Long's class to import large text files line by line. | |
//https://stackoverflow.com/questions/3707427/how-to-read-data-from-nsfilehandle-line-by-line/3711079#3711079 | |
//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 | |
DDFileReader * reader = [[DDFileReader alloc] initWithFilePath:pathToMyFile]; | |
NSString * line = nil; | |
while ((line = [reader readLine])) { | |
NSLog(@"read line: %@", line); | |
} | |
[reader release]; | |
DDFileReader * reader = [[DDFileReader alloc] initWithFilePath:pathToMyFile]; | |
[reader enumerateLinesUsingBlock:^(NSString * line, BOOL * stop) { | |
NSLog(@"read line: %@", line); | |
}]; | |
[reader release]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment