Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cocoa/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 如何逐行从NSFileHandle读取数据?_Objective C_Cocoa_Nsfilehandle - Fatal编程技术网

Objective c 如何逐行从NSFileHandle读取数据?

Objective c 如何逐行从NSFileHandle读取数据?,objective-c,cocoa,nsfilehandle,Objective C,Cocoa,Nsfilehandle,我有一个文本文件,其中包含给定的数据 e、 g 现在我想逐行读取数据。这意味着我首先要阅读 PUFGUjVRallYZDNaazFtVjVObU1zWm5ZcUJUYU5ORk4zbGthNHNDVUdSMlFVQmpSVEoxUUNSallYaFhkanBITXBGR1NTQnpZRTltZE1OalVzSkdXQ0Z6WXR0V2RpTmpTdXgwTWs5V1lZSkZiWjFXT29OV2JSVlhaSTUwYUpwR040UUZXTzVHVXFoWFVRcFdWNHdVTUJ0Q1VH

我有一个文本文件,其中包含给定的数据

e、 g

现在我想逐行读取数据。这意味着我首先要阅读

PUFGUjVRallYZDNaazFtVjVObU1zWm5ZcUJUYU5ORk4zbGthNHNDVUdSMlFVQmpSVEoxUUNSallYaFhkanBITXBGR1NTQnpZRTltZE1OalVzSkdXQ0Z6WXR0V2RpTmpTdXgwTWs5V1lZSkZiWjFXT29OV2JSVlhaSTUwYUpwR040UUZXTzVHVXFoWFVRcFdWNHdVTUJ0Q1VHSmxXVlJVTlJCMVE1VTFWV
然后剩下下一个。 有人知道吗

NSString *fh = [NSString stringWithContentsOfFile:filePath encoding:fileEncoding error:NULL];
for (NSString *line in [fh componentsSeparatedByString:@"\n"]) {
    // Do something with the line
}

Cocoa中没有API,也没有内置的语言结构,可以逐行读取文件。

如果文件很小,那么@mipadi的方法可能就可以了。但是,如果您的文件很大(也许是1MB,那么),那么您可能需要考虑逐行读取该文件。我写过一次这样的类,我将粘贴在这里:

//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
或(对于10.6+和iOS 4+):


我根据的源代码启动了一个。欢迎您改进代码。现在我可以前后读取文件了。

我已经将FileReader修改为NSFileHandle类别,希望它能帮助其他人

@interface NSFileHandle (Readline)
- (NSString*)readLine;
- (NSString*)readLineBackwards;
@end

#import "NSFileHandle+Readline.h"
#import "NSDataExtensions.h"

@implementation NSFileHandle (Readline)

- (NSString*)readLine {

    NSString * _lineDelimiter = @"\n";

    NSData* newLineData = [_lineDelimiter dataUsingEncoding:NSUTF8StringEncoding];
    NSMutableData* currentData = [[NSMutableData alloc] init];
    BOOL shouldReadMore = YES;

    NSUInteger _chunkSize = 10;

    while (shouldReadMore) {
        NSData* chunk = [self readDataOfLength:_chunkSize]; // always length = 10

        if ([chunk length] == 0) {
            break;
        }

        // Find the location and length of the next line delimiter.
        NSRange newLineRange = [chunk rangeOfData:newLineData];
        if (newLineRange.location != NSNotFound) {
            // Include the length so we can include the delimiter in the string.
            NSRange subDataRange = NSMakeRange(0, newLineRange.location + [newLineData length]);
            unsigned long long newOffset = [self offsetInFile] - [chunk length] + newLineRange.location + [newLineData length];
            [self seekToFileOffset:newOffset];
            chunk = [chunk subdataWithRange:subDataRange];
            shouldReadMore = NO;
        }
        [currentData appendData:chunk];
    }

    NSString* line = [currentData stringValueWithEncoding:NSASCIIStringEncoding];
    return line;
}

- (NSString*)readLineBackwards {

    NSString * _lineDelimiter = @"\n";

    NSData* newLineData = [_lineDelimiter dataUsingEncoding:NSUTF8StringEncoding];
    NSUInteger _chunkSize = 10;

    NSMutableData* currentData = [[NSMutableData alloc] init];
    BOOL shouldReadMore = YES;

    while (shouldReadMore) {

        unsigned long long offset;

        NSUInteger currentChunkSize = _chunkSize;

        if ([self offsetInFile] <= _chunkSize) {
            offset = 0;
            currentChunkSize = [self offsetInFile];
            shouldReadMore = NO;
        } else {
            offset = [self offsetInFile] - _chunkSize;
        }

        NSLog(@"seek to offset %qu, offset in file is %qu", offset, [self offsetInFile]);

        [self seekToFileOffset:offset];

        NSData* chunk = [self readDataOfLength:currentChunkSize];

        NSRange newLineRange = [chunk rangeOfDataBackwardsSearch:newLineData];

        if (newLineRange.location == NSNotFound) {
            [self seekToFileOffset:offset];
        }

        if (newLineRange.location != NSNotFound) {
            NSUInteger subDataLoc = newLineRange.location;
            NSUInteger subDataLen = currentChunkSize - subDataLoc;
            chunk = [chunk subdataWithRange:NSMakeRange(subDataLoc, subDataLen)];
            NSLog(@"got chunk data %@", [chunk stringValueWithEncoding:NSASCIIStringEncoding]);
            shouldReadMore = NO;
            [self seekToFileOffset:offset + newLineRange.location];
        }
        [currentData prepend:chunk];
    }

    NSString* line = [[NSString alloc] initWithData:currentData encoding:NSASCIIStringEncoding];
    return [line autorelease];
}

@end





//
//  NSDataExtensions.m
//  LineReader
//
//  Created by Tobias Preuss on 08.10.10.
//  Copyright 2010 Tobias Preuss. All rights reserved.
//

#import "NSDataExtensions.h"



// -----------------------------------------------------------------------------
// NSData additions.
// -----------------------------------------------------------------------------


/**
 Extension of the NSData class. 
 Data can be found forwards or backwards. Further the extension supplies a function 
 to convert the contents to string for debugging purposes.
 @param Additions Category labeled Additions.
 @returns An initialized NSData object or nil if the object could not be created.
 */
@implementation NSData (Additions)




/**
 Returns a range of data.
 @param dataToFind Data object specifying the delimiter and encoding.
 @returns A range.
 */
- (NSRange)rangeOfData:(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++) {
        // The current character matches.
        if (((char*)bytes)[index] == ((char*)searchBytes)[searchIndex]) {
            // Store found location if not done earlier.
            if (foundRange.location == NSNotFound) {
                foundRange.location = index;
            }
            // Increment search character index to check for match.
            searchIndex++;
            // All search character match.
            // Break search routine and return found position.
            if (searchIndex >= searchLength) {
                return foundRange;
            }
        }
        // Match does not continue.
        // Return to the first search character.
        // Discard former found location.
        else {
            searchIndex = 0;
            foundRange.location = NSNotFound;
        }
    }
    return foundRange;
}


- (NSRange)rangeOfDataBackwardsSearch:(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};
    if (length < searchLength) {
        return foundRange;
    }
    for (NSUInteger index = length - searchLength; index >= 0;) {
//      NSLog(@"%c == %c", ((char*)bytes)[index], ((char*)searchBytes)[searchIndex]); /* DEBUG LOG */
        if (((char*)bytes)[index] == ((char*)searchBytes)[searchIndex]) {
            // The current character matches.
            if (foundRange.location == NSNotFound) {
                foundRange.location = index;
            }
            index++;
            searchIndex++;
            if (searchIndex >= searchLength) {
                return foundRange;
            }
        }
        else {
            // Decrement to search backwards.
            if (foundRange.location == NSNotFound) {
                // Skip if first byte has been reached.
                if (index == 0) {
                    foundRange.location = NSNotFound;
                    return foundRange;
                }
                index--;
            }
            // Jump over the former found location
            // to avoid endless loop.
            else {
                index = index - 2;
            }
            searchIndex = 0;
            foundRange.location = NSNotFound;
        }
    }
    return foundRange;
}

- (NSString*)stringValueWithEncoding:(NSStringEncoding)encoding {
    return [[NSString alloc] initWithData:self encoding:encoding];
}

@end




// -----------------------------------------------------------------------------
// NSMutableData additions.
// -----------------------------------------------------------------------------


/**
 Extension of the NSMutableData class. 
 Data can be prepended in addition to the append function of the framework.
 @param Additions Category labeled Additions.
 @returns An initialized NSMutableData object or nil if the object could not be created.
 */
@implementation NSMutableData (Additions)

/**
    Inserts the data before the data of the object.
    @param data Data to be prepended.
 */
- (void)prepend:(NSData*)data {


    NSMutableData* concat = [NSMutableData dataWithData:data];
    [concat appendData:self];
    [self setData:concat];
}

@end
@接口NSFileHandle(Readline)
-(NSString*)读线;
-(NSString*)readLineBackwards;
@结束
#导入“NSFileHandle+Readline.h”
#导入“NSDataExtensions.h”
@实现NSFileHandle(Readline)
-(NSString*)读线{
NSString*_lineDelimiter=@“\n”;
NSData*newLineData=[\u lineDelimiter dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData*currentData=[[NSMutableData alloc]init];
BOOL shouldReadMore=是;
NSU整数_chunkSize=10;
while(shouldReadMore){
NSData*chunk=[self-readDataOfLength:_chunkSize];//始终长度=10
如果([chunk length]==0){
打破
}
//查找下一行分隔符的位置和长度。
NSRange newLineRange=[chunk rangeOfData:newLineData];
if(newLineRange.location!=NSNotFound){
//包含长度,以便在字符串中包含分隔符。
NSRange subDataRange=NSMakeRange(0,newLineRange.location+[newLineData length]);
unsigned long long newOffset=[self offsetInFile]-[chunk length]+newLineRange.location+[newLineData length];
[self-seekToFileOffset:newOffset];
chunk=[chunk subdataWithRange:subDataRange];
shouldReadMore=否;
}
[currentData appendData:chunk];
}
NSString*line=[currentData stringValueWithEncoding:NSASCIIStringEncoding];
回流线;
}
-(NSString*)readLineBackwards{
NSString*_lineDelimiter=@“\n”;
NSData*newLineData=[\u lineDelimiter dataUsingEncoding:NSUTF8StringEncoding];
NSU整数_chunkSize=10;
NSMutableData*currentData=[[NSMutableData alloc]init];
BOOL shouldReadMore=是;
while(shouldReadMore){
无符号长偏移量;
NSUTEGER currentChunkSize=\u chunkSize;
if([self offsetInFile]=searchLength){
返回范围;
}
}
//比赛不继续。
//返回到第一个搜索字符。
//放弃以前找到的位置。
否则{
searchIndex=0;
foundRange.location=NSNotFound;
}
}
返回范围;
}
-(NSRange)数据备份范围搜索:(NSData*)数据查找{
const void*字节=[self bytes];
NSU整数长度=[自身长度];
const void*searchBytes=[dataToFind bytes];
NSUTEGER searchLength=[dataToFind length];
整数搜索索引=0;
NSRange foundRange={NSNotFound,searchLength};
如果(长度<搜索长度){
返回范围;
}
对于(整数索引=长度-搜索长度;索引>=0;){
//NSLog(@“%c==%c”,((char*)字节)[index],((char*)searchBytes)[searchIndex]);/*调试日志*/
如果(((char*)字节)[index]==((char*)searchBytes)[searchIndex]){
//当前字符匹配。
if(foundRange.location==NSNotFound){
foundRange.location=索引;
}
索引++;
searchIndex++;
如果(searchIndex>=searchLength){
返回范围;
}
}
否则{
//递减以向后搜索。
if(foundRange.location==NSNotFound){
//如果已到达第一个字节,则跳过。
如果(索引==0){
foundRange.location=NSNotFound;
返回范围;
}
索引--;
}
//跳过以前找到的位置
//避免无休止的循环。
否则{
指数=指数-2;
}
searchIndex=0;
foundRange.location=NSNotFound;
}
}
返回范围;
}
-(NSString*)stringValueWithEncoding:(NSStringEncoding)encoding{
return[[NSString alloc]initWithData:self-encoding:encoding];
}
@结束
// -----------------------------------------------------------------------------
//NSMutableData添加。
// -----------------------------------------------------------------------------
/**
NSMutableData类的扩展。
除了框架的append函数外,还可以预先添加数据。
@param Additions类别标记为Additions。
@返回初始化的NSMutableData对象,如果无法创建该对象,则返回nil。
*/
@NSMutableData的实现(添加)
/**
在对象的数据之前插入数据。
@要加前缀的参数数据。
*/
-(void)前置:(NSData*)数据{
NSMutableData*concat=[NSMutableData-dataWithData:data];
[concat-appendData:self];
[自设置数据:concat];
}
@结束
我发现它也使用读线器。
签出或

@Joe Yang
美好的接下来几天我会仔细看看。

如果您想分叉向我发送拉取请求,我将非常高兴

我将此重写为符合ARC要求:

//
//  DDFileReader.m
//  PBX2OPML
//
//  Created by michael isbell on 11/6/11.
//  Copyright (c) 2011 BlueSwitch. All rights reserved.
//

//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) {
            return nil;
        }

        lineDelimiter = @"\n";
        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];
    currentOffset = 0ULL;

}

- (NSString *) readLine {
    if (currentOffset >= totalFileLength) { return nil; }

    NSData * newLineData = [lineDelimiter dataUsingEncoding:NSUTF8StringEncoding];
    [fileHandle seekToFileOffset:currentOffset];
    NSMutableData * currentData = [[NSMutableData alloc] init];
    BOOL shouldReadMore = YES;

    @autoreleasepool {

    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];
    }
    }

    NSString * line = [[NSString alloc] initWithData:currentData encoding:NSUTF8StringEncoding];
    return line;  
}

- (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.m
//PBX2OPML
//
//由迈克尔·伊斯贝尔于2011年11月6日创作。
//版权所有(c)2011 BlueSwitch。版权所有。
//
//DDFileReader.m
#导入“DDFileRea”
DDFileReader * reader = [[DDFileReader alloc] initWithFilePath:pathToMyFile];
[reader enumerateLinesUsingBlock:^(NSString * line, BOOL * stop) {
  NSLog(@"read line: %@", line);
}];
[reader release];
@interface NSFileHandle (Readline)
- (NSString*)readLine;
- (NSString*)readLineBackwards;
@end

#import "NSFileHandle+Readline.h"
#import "NSDataExtensions.h"

@implementation NSFileHandle (Readline)

- (NSString*)readLine {

    NSString * _lineDelimiter = @"\n";

    NSData* newLineData = [_lineDelimiter dataUsingEncoding:NSUTF8StringEncoding];
    NSMutableData* currentData = [[NSMutableData alloc] init];
    BOOL shouldReadMore = YES;

    NSUInteger _chunkSize = 10;

    while (shouldReadMore) {
        NSData* chunk = [self readDataOfLength:_chunkSize]; // always length = 10

        if ([chunk length] == 0) {
            break;
        }

        // Find the location and length of the next line delimiter.
        NSRange newLineRange = [chunk rangeOfData:newLineData];
        if (newLineRange.location != NSNotFound) {
            // Include the length so we can include the delimiter in the string.
            NSRange subDataRange = NSMakeRange(0, newLineRange.location + [newLineData length]);
            unsigned long long newOffset = [self offsetInFile] - [chunk length] + newLineRange.location + [newLineData length];
            [self seekToFileOffset:newOffset];
            chunk = [chunk subdataWithRange:subDataRange];
            shouldReadMore = NO;
        }
        [currentData appendData:chunk];
    }

    NSString* line = [currentData stringValueWithEncoding:NSASCIIStringEncoding];
    return line;
}

- (NSString*)readLineBackwards {

    NSString * _lineDelimiter = @"\n";

    NSData* newLineData = [_lineDelimiter dataUsingEncoding:NSUTF8StringEncoding];
    NSUInteger _chunkSize = 10;

    NSMutableData* currentData = [[NSMutableData alloc] init];
    BOOL shouldReadMore = YES;

    while (shouldReadMore) {

        unsigned long long offset;

        NSUInteger currentChunkSize = _chunkSize;

        if ([self offsetInFile] <= _chunkSize) {
            offset = 0;
            currentChunkSize = [self offsetInFile];
            shouldReadMore = NO;
        } else {
            offset = [self offsetInFile] - _chunkSize;
        }

        NSLog(@"seek to offset %qu, offset in file is %qu", offset, [self offsetInFile]);

        [self seekToFileOffset:offset];

        NSData* chunk = [self readDataOfLength:currentChunkSize];

        NSRange newLineRange = [chunk rangeOfDataBackwardsSearch:newLineData];

        if (newLineRange.location == NSNotFound) {
            [self seekToFileOffset:offset];
        }

        if (newLineRange.location != NSNotFound) {
            NSUInteger subDataLoc = newLineRange.location;
            NSUInteger subDataLen = currentChunkSize - subDataLoc;
            chunk = [chunk subdataWithRange:NSMakeRange(subDataLoc, subDataLen)];
            NSLog(@"got chunk data %@", [chunk stringValueWithEncoding:NSASCIIStringEncoding]);
            shouldReadMore = NO;
            [self seekToFileOffset:offset + newLineRange.location];
        }
        [currentData prepend:chunk];
    }

    NSString* line = [[NSString alloc] initWithData:currentData encoding:NSASCIIStringEncoding];
    return [line autorelease];
}

@end





//
//  NSDataExtensions.m
//  LineReader
//
//  Created by Tobias Preuss on 08.10.10.
//  Copyright 2010 Tobias Preuss. All rights reserved.
//

#import "NSDataExtensions.h"



// -----------------------------------------------------------------------------
// NSData additions.
// -----------------------------------------------------------------------------


/**
 Extension of the NSData class. 
 Data can be found forwards or backwards. Further the extension supplies a function 
 to convert the contents to string for debugging purposes.
 @param Additions Category labeled Additions.
 @returns An initialized NSData object or nil if the object could not be created.
 */
@implementation NSData (Additions)




/**
 Returns a range of data.
 @param dataToFind Data object specifying the delimiter and encoding.
 @returns A range.
 */
- (NSRange)rangeOfData:(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++) {
        // The current character matches.
        if (((char*)bytes)[index] == ((char*)searchBytes)[searchIndex]) {
            // Store found location if not done earlier.
            if (foundRange.location == NSNotFound) {
                foundRange.location = index;
            }
            // Increment search character index to check for match.
            searchIndex++;
            // All search character match.
            // Break search routine and return found position.
            if (searchIndex >= searchLength) {
                return foundRange;
            }
        }
        // Match does not continue.
        // Return to the first search character.
        // Discard former found location.
        else {
            searchIndex = 0;
            foundRange.location = NSNotFound;
        }
    }
    return foundRange;
}


- (NSRange)rangeOfDataBackwardsSearch:(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};
    if (length < searchLength) {
        return foundRange;
    }
    for (NSUInteger index = length - searchLength; index >= 0;) {
//      NSLog(@"%c == %c", ((char*)bytes)[index], ((char*)searchBytes)[searchIndex]); /* DEBUG LOG */
        if (((char*)bytes)[index] == ((char*)searchBytes)[searchIndex]) {
            // The current character matches.
            if (foundRange.location == NSNotFound) {
                foundRange.location = index;
            }
            index++;
            searchIndex++;
            if (searchIndex >= searchLength) {
                return foundRange;
            }
        }
        else {
            // Decrement to search backwards.
            if (foundRange.location == NSNotFound) {
                // Skip if first byte has been reached.
                if (index == 0) {
                    foundRange.location = NSNotFound;
                    return foundRange;
                }
                index--;
            }
            // Jump over the former found location
            // to avoid endless loop.
            else {
                index = index - 2;
            }
            searchIndex = 0;
            foundRange.location = NSNotFound;
        }
    }
    return foundRange;
}

- (NSString*)stringValueWithEncoding:(NSStringEncoding)encoding {
    return [[NSString alloc] initWithData:self encoding:encoding];
}

@end




// -----------------------------------------------------------------------------
// NSMutableData additions.
// -----------------------------------------------------------------------------


/**
 Extension of the NSMutableData class. 
 Data can be prepended in addition to the append function of the framework.
 @param Additions Category labeled Additions.
 @returns An initialized NSMutableData object or nil if the object could not be created.
 */
@implementation NSMutableData (Additions)

/**
    Inserts the data before the data of the object.
    @param data Data to be prepended.
 */
- (void)prepend:(NSData*)data {


    NSMutableData* concat = [NSMutableData dataWithData:data];
    [concat appendData:self];
    [self setData:concat];
}

@end
//
//  DDFileReader.m
//  PBX2OPML
//
//  Created by michael isbell on 11/6/11.
//  Copyright (c) 2011 BlueSwitch. All rights reserved.
//

//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) {
            return nil;
        }

        lineDelimiter = @"\n";
        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];
    currentOffset = 0ULL;

}

- (NSString *) readLine {
    if (currentOffset >= totalFileLength) { return nil; }

    NSData * newLineData = [lineDelimiter dataUsingEncoding:NSUTF8StringEncoding];
    [fileHandle seekToFileOffset:currentOffset];
    NSMutableData * currentData = [[NSMutableData alloc] init];
    BOOL shouldReadMore = YES;

    @autoreleasepool {

    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];
    }
    }

    NSString * line = [[NSString alloc] initWithData:currentData encoding:NSUTF8StringEncoding];
    return line;  
}

- (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
- (NSString*) readLine: (NSInputStream*) inputStream {
    NSMutableData* data = [NSMutableData data];
    uint8_t oneByte;
    do {
        int actuallyRead = [inputStream read: &oneByte maxLength: 1];
        if (actuallyRead == 1) {
            [data appendBytes: &oneByte length: 1];
        }        
    } while (oneByte != '\n');

    return [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
FILE* file = fopen("path to my file", "r");

size_t length;
char *cLine = fgetln(file,&length);

while (length>0) {
    char str[length+1];
    strncpy(str, cLine, length);
    str[length] = '\0';

    NSString *line = [NSString stringWithFormat:@"%s",str];        
    % Do what you want here.

    cLine = fgetln(file,&length);
}
extension FileHandle {

    func enumerateLines(_ block: @escaping (String, UnsafeMutablePointer<Bool>) -> Void) {

        // find the end of file
        var offset = self.offsetInFile
        let eof = self.seekToEndOfFile()
        self.seek(toFileOffset: offset)
        let blockSize = 1024
        var buffer = Data()

        // process to the end of file
        while offset + UInt64(buffer.count) < eof {
            var found = false

            // make sure buffer contains at least one CR, LF or null
            while !found && offset + UInt64(buffer.count) < eof {
                let block = self.readData(ofLength: blockSize)
                buffer.append(block)
                for byte in block {
                    if [0x0d, 0x0a, 0x00].contains(byte) {
                        found = true ; break
                    }
                }
            }

            // retrieve lines within the buffer
            var index = 0
            var head = 0 // head of line
            var done = false
            buffer.enumerateBytes({ (pointer, count, stop) in
                while index < count {
                    // find a line terminator
                    if [0x0d, 0x0a, 0x00].contains(pointer[index]) {
                        let lineData = Data(pointer[head ..< index])
                        if let line = String(bytes: lineData, encoding: .utf8) {
                            block(line, &stop) // stop requested
                            if pointer[index] == 0x0d && index+1 < count && pointer[index+1] == 0x0a {
                                index += 2 ; head = index
                            }
                            else { index += 1 ; head = index }
                            if stop { done = true ; return } // end of enumerateLines
                        }
                        else { return } // end of enumerateLines
                    }
                    else { index += 1 }
                }
            })

            offset += UInt64(head)
            buffer.replaceSubrange(0 ..< head, with: Data())
            if done { // stop requested
                self.seek(toFileOffset: offset)
                return
            }
        }
    }
    let fileURL = Bundle.main.url(forResource: "huge_file", withExtension: "txt")!
    let fileHandle = try! FileHandle(forReadingFrom: fileURL)

    fileHandle.enumerateLines { (line, stop) in
        if someCondition { stop.pointee = true }
        print(line)
    }
    /* let remaining = fileHandle.readDataToEndOfFile() */