Objective-C:逐行读取文件


140

在Objective-C中处理大型文本文件的适当方法是什么?假设我需要分别阅读每一行,并希望将每一行都视为NSString。最有效的方法是什么?

一种解决方案是使用NSString方法:

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

然后使用换行符分隔线,然后遍历数组中的元素。但是,这似乎效率很低。没有简单的方法将文件视为流,枚举每一行,而不是一次全部读取吗?有点像Java的java.io.BufferedReader。


1
有点晚了,但是假设您想将每行读入字符串“ read”,请签出[NSScanner scanUpToString:@“ \ n” intoString:&read]。
hauntsaninja

请看看这个类似的问题。我建立了一个处理逐行读取文件的项目。
JJD 2012年

Answers:


63

这是一个很好的问题。我认为@Diederik有一个很好的答案,尽管很遗憾,可可没有确切的机制来满足您的需求。

NSInputStream允许您读取N个字节的数据块(与相似java.io.BufferedReader),但是您必须将其转换为一个字节NSString,然后扫描换行符(或任何其他定界符)并保存所有剩余字符以供下次读取,或读取更多字符如果尚未读取换行符。(NSFileHandle让您阅读NSData,然后可以将其转换为NSString,但是本质上是相同的过程。)

Apple提供了一个Stream编程指南,可以帮助您填写详细信息,因此,如果您要处理缓冲区,那么这个SO问题也可能会有所帮助uint8_t*

如果您要经常读取这样的字符串(尤其是在程序的不同部分),则最好将此行为封装在一个可以为您处理细节甚至是子类的类中NSInputStream(它被设计为子类)并添加方法,使您可以准确阅读所需内容。

作为记录,我认为这将是一个不错的功能,我将提出增强请求,以使之成为可能。:-)


编辑:原来此请求已存在。为此有一个可追溯到2006年的Radar(对于Apple内部人员,rdar:// 4742914)。


10
请参阅Dave DeLong针对此问题的综合方法: stackoverflow.com/questions/3707427#3711079
Quinn Taylor

也可以使用普通的NSData和内存映射。我已创建了示例代码的答案具有相同的API戴维德隆的NSFileHandle实现:stackoverflow.com/a/21267461/267043
比约恩奥拉夫·路德·

95

这将一般阅读一个工作StringText。如果您想阅读较长的文本(较大的文本),请使用此处提到的其他方法,例如缓冲(在内存空间中保留文本的大小)

假设您阅读了一个文本文件。

NSString* filePath = @""//file path...
NSString* fileRoot = [[NSBundle mainBundle] 
               pathForResource:filePath ofType:@"txt"];

您想摆脱换行。

// read everything from text
NSString* fileContents = 
      [NSString stringWithContentsOfFile:fileRoot 
       encoding:NSUTF8StringEncoding error:nil];

// first, separate by new line
NSArray* allLinedStrings = 
      [fileContents componentsSeparatedByCharactersInSet:
      [NSCharacterSet newlineCharacterSet]];

// then break down even further 
NSString* strsInOneLine = 
      [allLinedStrings objectAtIndex:0];

// choose whatever input identity you have decided. in this case ;
NSArray* singleStrs = 
      [currentPointString componentsSeparatedByCharactersInSet:
      [NSCharacterSet characterSetWithCharactersInString:@";"]];

你有它。


17
我有一个70 mb的文件,使用此代码读取文件不会让我失望,它会线性增加内存。谁能帮我?
GameLoading 2011年

37
这不是对该问题的回答。问题是逐行读取文件以减少内存使用量
doozMen 2012年

34

这应该可以解决问题:

#include <stdio.h>

NSString *readLineAsNSString(FILE *file)
{
    char buffer[4096];

    // tune this capacity to your liking -- larger buffer sizes will be faster, but
    // use more memory
    NSMutableString *result = [NSMutableString stringWithCapacity:256];

    // Read up to 4095 non-newline characters, then read and discard the newline
    int charsRead;
    do
    {
        if(fscanf(file, "%4095[^\n]%n%*c", buffer, &charsRead) == 1)
            [result appendFormat:@"%s", buffer];
        else
            break;
    } while(charsRead == 4095);

    return result;
}

用法如下:

FILE *file = fopen("myfile", "r");
// check for NULL
while(!feof(file))
{
    NSString *line = readLineAsNSString(file);
    // do stuff with line; line is autoreleased, so you should NOT release it (unless you also retain it beforehand)
}
fclose(file);

此代码从文件中读取非换行符,一次最多读取4095个字符。如果一行的长度超过4095个字符,它将一直保持读取状态,直到遇到换行符或文件结尾为止。

注意:我尚未测试此代码。使用前请先进行测试。


1
只需更改[结果appendFormat:“%s”,缓冲区]; 到[结果appendFormat:@“%s”,缓冲区];
Codezy

1
您将如何修改格式以接受空行,或更确切地说是由单个换行符组成的行?
jakev

在812行之后,这对于我来说早早停止了。812行是“ ... 3 more”,这使阅读器输出空字符串。
sudo 2013年

1
我添加了一个检查以跳过空行:int fscanResult = fscanf(file,“%4095 [^ \ n]%n%* c”,buffer,&charsRead); if(fscanResult == 1){[结果appendFormat:@“%s”,缓冲区]; } else {if(feof(file)){break; } else if(ferror(file)!= 0){break; } fscanf(file,“ \ n”,nil,&charsRead); 打破; }
Go Rose-Hulman 2014年

1
如果我没看错fscanf文档,"%4095[^\n]%n%*c"则每次读取缓冲区时,它都会静默消耗并丢弃一个字符。看起来这种格式假设行将比缓冲区长度短。
Blago

12

Mac OS X是Unix,Objective-C是C超集,因此您可以只使用old-class fopenfgetsfrom <stdio.h>。保证可以正常工作。

[NSString stringWithUTF8String:buf]将C字符串转换为NSString。还有一些方法可以创建其他编码形式的字符串,并且无需复制即可创建。


[复制匿名评论] fgets将包含该'\n'字符,因此您可能希望在转换字符串之前先将其删除。
Kornel

9

您可以使用NSInputStream具有基本实现的文件流。您可以将字节读入缓冲区(read:maxLength:方法)。您必须自己扫描缓冲区以查找换行符。


6

Apple的String编程指南中介绍了在Cocoa / Objective-C中读取文本文件的适当方法。读写文件的部分应该就是您要做的。PS:什么是“线”?字符串的两个部分用“ \ n”分隔?还是“ \ r”?还是“ \ r \ n”?或者,也许您实际上是在段落后面?前面提到的指南还包括有关将字符串拆分为行或段的部分。(此部分称为“段落和换行符”,并链接到我上面指向的页面的左侧菜单中。不幸的是,此网站不允许我发布多个URL,因为我还不是可信赖的用户。)

解读Knuth:过早的优化是万恶之源。不要简单地假设“将整个文件读入内存”很慢。你有基准吗?您知道它实际上将整个文件读入内存吗?也许它只是返回一个代理对象,并在您使用该字符串时一直在幕后读取?(免责声明:我不知道NSString是否确实做到了。可以想象得到。)重点是:首先采用记录的处理方式。然后,如果基准测试表明它没有您想要的性能,请优化。


既然您提到了CRLF(Windows)行尾:实际上,这是一种破坏Objective-C处理方式的情况。如果您使用的一个-stringWithContentsOf*方法之后-componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet],它看到\r\n分别与各行后添加一个空行。
西沃恩

也就是说,fgets解决方案在仅限CR的文件上失败。但是,这些(在理论上)如今是罕见的,并且fgets确实适用于LF和CRLF。
西沃恩

6

这些答案很多都是很长的代码,或者它们读入了整个文件。我喜欢使用c方法完成这项任务。

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

请注意,fgetln不会保留换行符。另外,我们为str的长度+1,因为我们要为NULL终止留出空间。


4

可以通过以下功能逐行读取文件(对于大型文件也是如此):

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

启用此功能的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

实施(.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

该课程由Dave DeLong完成


4

就像@porneL所说的那样,C api非常方便。

NSString* fileRoot = [[NSBundle mainBundle] pathForResource:@"record" ofType:@"txt"];
FILE *file = fopen([fileRoot UTF8String], "r");
char buffer[256];
while (fgets(buffer, 256, file) != NULL){
    NSString* result = [NSString stringWithUTF8String:buffer];
    NSLog(@"%@",result);
}

4

正如其他人回答的那样,NSInputStream和NSFileHandle都是不错的选择,但也可以使用NSData和内存映射以相当紧凑的方式完成:

BRLineReader.h

#import <Foundation/Foundation.h>

@interface BRLineReader : NSObject

@property (readonly, nonatomic) NSData *data;
@property (readonly, nonatomic) NSUInteger linesRead;
@property (strong, nonatomic) NSCharacterSet *lineTrimCharacters;
@property (readonly, nonatomic) NSStringEncoding stringEncoding;

- (instancetype)initWithFile:(NSString *)filePath encoding:(NSStringEncoding)encoding;
- (instancetype)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding;
- (NSString *)readLine;
- (NSString *)readTrimmedLine;
- (void)setLineSearchPosition:(NSUInteger)position;

@end

BRLineReader.m

#import "BRLineReader.h"

static unsigned char const BRLineReaderDelimiter = '\n';

@implementation BRLineReader
{
    NSRange _lastRange;
}

- (instancetype)initWithFile:(NSString *)filePath encoding:(NSStringEncoding)encoding
{
    self = [super init];
    if (self) {
        NSError *error = nil;
        _data = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedAlways error:&error];
        if (!_data) {
            NSLog(@"%@", [error localizedDescription]);
        }
        _stringEncoding = encoding;
        _lineTrimCharacters = [NSCharacterSet whitespaceAndNewlineCharacterSet];
    }

    return self;
}

- (instancetype)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding
{
    self = [super init];
    if (self) {
        _data = data;
        _stringEncoding = encoding;
        _lineTrimCharacters = [NSCharacterSet whitespaceAndNewlineCharacterSet];
    }

    return self;
}

- (NSString *)readLine
{
    NSUInteger dataLength = [_data length];
    NSUInteger beginPos = _lastRange.location + _lastRange.length;
    NSUInteger endPos = 0;
    if (beginPos == dataLength) {
        // End of file
        return nil;
    }

    unsigned char *buffer = (unsigned char *)[_data bytes];
    for (NSUInteger i = beginPos; i < dataLength; i++) {
        endPos = i;
        if (buffer[i] == BRLineReaderDelimiter) break;
    }

    // End of line found
    _lastRange = NSMakeRange(beginPos, endPos - beginPos + 1);
    NSData *lineData = [_data subdataWithRange:_lastRange];
    NSString *line = [[NSString alloc] initWithData:lineData encoding:_stringEncoding];
    _linesRead++;

    return line;
}

- (NSString *)readTrimmedLine
{
    return [[self readLine] stringByTrimmingCharactersInSet:_lineTrimCharacters];
}

- (void)setLineSearchPosition:(NSUInteger)position
{
    _lastRange = NSMakeRange(position, 0);
    _linesRead = 0;
}

@end

1

这个答案不是ObjC,而是C。

由于ObjC基于'C',为什么不使用fgets?

是的,我确定ObjC拥有自己的方法-我只是还不够熟练,还不知道它是什么:)


5
如果您不知道如何在Objective-C中做到这一点,那为什么不能说这不是答案呢?如果没有其他理由,有很多理由不降为直线C。例如,C函数处理char *,但是读取其他内容(例如不同的编码)需要花费更多的工作。另外,他想要NSString对象。总而言之,自己动手滚动不仅要编写更多代码,而且还容易出错。
奎因·泰勒,

3
我100%同意您的意见,但我发现(有时)最好迅速找到可行的答案,加以实施,然后在出现更正确的选择时,加以利用。在进行原型制作时,这尤其重要,它使您有机会进行一些工作,然后从那里进行开发。
KevinDTimm,2009年

3
我只是意识到它始于“这个答案”而不是“答案”。h!我同意,拥有一个有效的hack绝对比不起作用的优雅代码更好。我没有投票给你,但是丢掉一个猜测,而不知道Objective-C可能不是很有帮助。即使这样,努力总是比知道和不帮助的人更好…;-)
Quinn Taylor

这不能为问题提供答案。要批评或要求作者澄清,请在其帖子下方发表评论。
机器人猫

1
@KevinDTimm:我同意;我很抱歉,我没有发现这是一个5岁的答案。也许这是一个meta问题;来自普通用户的非常老的问题是否应该被标记为可以复审?
机器人猫

0

从@Adam Rosenfield的答案中,其格式字符串fscanf将如下更改:

"%4095[^\r\n]%n%*[\n\r]"

它可以在osx,linux,windows行尾使用。


0

使用类别或扩展名可以使我们的生活更轻松。

extension String {

    func lines() -> [String] {
        var lines = [String]()
        self.enumerateLines { (line, stop) -> () in
            lines.append(line)
        }
        return lines
    }

}

// then
for line in string.lines() {
    // do the right thing
}

0

我发现@lukaswelte的回复以及Dave DeLong的代码很有帮助。我一直在寻找解决这个问题,但通过需要解析大文件\r\n不仅仅是\n

如果通过多个字符进行解析,则所编写的代码将包含一个错误。我更改了如下代码。

.h文件:

#import <Foundation/Foundation.h>

@interface FileChunkReader : 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

.m文件:

#import "FileChunkReader.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;
        }
    }

    if (foundRange.location != NSNotFound
        && length < foundRange.location + foundRange.length )
    {
        // if the dataToFind is partially found at the end of [self bytes],
        // then the loop above would end, and indicate the dataToFind is found
        // when it only partially was.
        foundRange.location = NSNotFound;
    }

    return foundRange;
}

@end

@implementation FileChunkReader

@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 = 128;
        [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;
    }

    @autoreleasepool {

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


        while (shouldReadMore) {
            if (currentOffset >= totalFileLength)
            {
                break;
            }

            NSData * chunk = [fileHandle readDataOfLength:chunkSize];
            [currentData appendData:chunk];

            NSRange newLineRange = [currentData rangeOfData_dd:newLineData];

            if (newLineRange.location != NSNotFound) {

                currentOffset = originalOffset + newLineRange.location + newLineData.length;
                currentLine = [currentData subdataWithRange:NSMakeRange(0, newLineRange.location)];

                shouldReadMore = NO;
            }else{
                currentOffset += [chunk length];
            }
        }

        if (currentLine.length == 0 && currentData.length > 0)
        {
            currentLine = currentData;
        }

        return [[NSString alloc] initWithData:currentLine encoding:NSUTF8StringEncoding];
    }
}

- (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

0

我添加此内容是因为我尝试过的所有其他答案都以某种方式无法实现。以下方法可以处理大文件,任意长行以及空行。它已经过实际内容测试,将从输出中去除换行符。

- (NSString*)readLineFromFile:(FILE *)file
{
    char buffer[4096];
    NSMutableString *result = [NSMutableString stringWithCapacity:1000];

    int charsRead;
    do {
        if(fscanf(file, "%4095[^\r\n]%n%*[\n\r]", buffer, &charsRead) == 1) {
            [result appendFormat:@"%s", buffer];
        }
        else {
            break;
        }
    } while(charsRead == 4095);

    return result.length ? result : nil;
}

归功于@Adam Rosenfield和@sooop


0

我看到许多这样的答案依赖于将整个文本文件读取到内存中,而不是一次将其占用一块。这是我在现代的Swift中的解决方案,使用FileHandle来降低内存影响:

enum MyError {
    case invalidTextFormat
}

extension FileHandle {

    func readLine(maxLength: Int) throws -> String {

        // Read in a string of up to the maximum length
        let offset = offsetInFile
        let data = readData(ofLength: maxLength)
        guard let string = String(data: data, encoding: .utf8) else {
            throw MyError.invalidTextFormat
        }

        // Check for carriage returns; if none, this is the whole string
        let substring: String
        if let subindex = string.firstIndex(of: "\n") {
            substring = String(string[string.startIndex ... subindex])
        } else {
            substring = string
        }

        // Wind back to the correct offset so that we don't miss any lines
        guard let dataCount = substring.data(using: .utf8, allowLossyConversion: false)?.count else {
            throw MyError.invalidTextFormat
        }
        try seek(toOffset: offset + UInt64(dataCount))
        return substring
    }

}

请注意,这将在行尾保留回车符,因此,根据您的需要,您可能需要调整代码以将其删除。

用法:只需打开目标文本文件的文件句柄并readLine以适当的最大长度进行调用-纯文本的标准大小为1024,但我将其保持打开状态以防您知道它会更短。请注意,该命令不会溢出文件的末尾,因此,如果您打算解析整个文件,则可能必须手动检查尚未到达文件的末尾。这是一些示例代码,显示了如何在处打开文件myFileURL并逐行读取直到结束。

do {
    let handle = try FileHandle(forReadingFrom: myFileURL)
    try handle.seekToEndOfFile()
    let eof = handle.offsetInFile
    try handle.seek(toFileOffset: 0)

    while handle.offsetInFile < eof {
        let line = try handle.readLine(maxLength: 1024)
        // Do something with the string here
    }
    try handle.close()
catch let error {
    print("Error reading file: \(error.localizedDescription)"
}

-2

这是我用于较小文件的一个很好的简单解决方案:

NSString *path = [[NSBundle mainBundle] pathForResource:@"Terrain1" ofType:@"txt"];
NSString *contents = [NSString stringWithContentsOfFile:path encoding:NSASCIIStringEncoding error:nil];
NSArray *lines = [contents componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\r\n"]];
for (NSString* line in lines) {
    if (line.length) {
        NSLog(@"line: %@", line);
    }
}

他在问如何一次读取一行,这样它就不会将全部内容读到内存中。您的解决方案将创建一个包含所有内容的字符串,然后将其拆分为几行。
大卫,

-7

使用此脚本,效果很好:

NSString *path = @"/Users/xxx/Desktop/names.txt";
NSError *error;
NSString *stringFromFileAtPath = [NSString stringWithContentsOfFile: path
                                                           encoding: NSUTF8StringEncoding
                                                              error: &error];
if (stringFromFileAtPath == nil) {
    NSLog(@"Error reading file at %@\n%@", path, [error localizedFailureReason]);
}
NSLog(@"Contents:%@", stringFromFileAtPath);

1
@fisninear的意思是,这不能满足OP减少内存使用的需求。OP并没有询问如何使用该方法(将整个文件加载到内存中),而是询问大型文本文件的内存友好替代方法。拥有数千兆字节的文本文件很有可能,这显然会造成内存问题。
Joshua Nozzi 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.