【问题标题】:How to read data from NSFileHandle line by line?如何逐行从 NSFileHandle 读取数据?
【发布时间】:2011-04-12 01:38:06
【问题描述】:

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

例如

PUFGUjVRallYZDNaazFtVjVObU1zWm5ZcUJUYU5ORk4zbGthNHNDVUdSMlFVQmpSVEoxUUNSallYaFhkanBITXBGR1NTQnpZRTltZE1OalVzSkdXQ0Z6WXR0V2RpTmpTdXgwTWs5V1lZSkZiWjFXT29OV2JSVlhaSTUwYUpwR040UUZXTzVHVXFoWFVRcFdWNHdVTUJ0Q1VHSmxXVlJVTlJCMVE1VTFWV
PUFGUjVRallYZDNaazFtVjVObU1zWm5ZcUJUYU5ORk4zbGthNHNDVUdSMlFVQmpSVEoxUUNSallYaFhkanBITXBGR1NTQnpZRTltZE1OalVzSkdXQ0Z6WXR0V2RpTmpTdXgwTWs5V1lZSkZiWjFXT29OV2JSVlhaSTUwYUpwR040UUZXTzVHVXFoWFVRcFdWNHdVTUJ0Q1VHSmxXVlJVTlJCMVE1VTFWV

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

PUFGUjVRallYZDNaazFtVjVObU1zWm5ZcUJUYU5ORk4zbGthNHNDVUdSMlFVQmpSVEoxUUNSallYaFhkanBITXBGR1NTQnpZRTltZE1OalVzSkdXQ0Z6WXR0V2RpTmpTdXgwTWs5V1lZSkZiWjFXT29OV2JSVlhaSTUwYUpwR040UUZXTzVHVXFoWFVRcFdWNHdVTUJ0Q1VHSmxXVlJVTlJCMVE1VTFWV

然后下一个。 有人知道吗??

【问题讨论】:

  • 我在逻辑上能够通过使用 NSArray 来实现这一点,并在换行符的基础上分离组件。
  • 但是有没有其他方法?任何 API?

标签: objective-c cocoa nsfilehandle


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

Cocoa 中没有用于逐行读取文件的 API 或内置语言结构。

【讨论】:

  • 别忘了 -[NSString enumerateLinesUsingBlock:],在 OS X 10.6 和 iOS 4 中可用。我相信它在存在换行符时会更加健壮,而不仅仅是 \n。跨度>
  • 感谢您的回答我已经这样做了,但是因为我不确定 API 是否存在,所以我问这个问题。
  • typo -> 组件应该读取组件。否则,它是读取小文件的干净简单的解决方案。谢谢:D
【解决方案2】:

如果您的文件很小,那么@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

然后要使用它,你会这样做:

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

或(适用于 10.6+ 和 iOS 4+):

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

【讨论】:

  • @OlivaresF:当然可以,但是您的内存中有全部 40MB 的文件。我的答案中发布的代码将逐块读取文件,并由您决定要保留多少(通过定期创建和排出NSAutoreleasePools)。
  • @Dave 这绝对是真的,只是想我会评论这是一个权衡。
  • @Will 本网站上发布的所有内容均在 CC-wiki 授权下。这是每一页的页脚。
  • @Dave 啊,好的。 CC 并不是一个很好的软件许可证,所以律师会养小猫。不过还是谢谢你。
  • 任何有问题的人请注意:“使用此”示例代码返回带有换行符的字符串!如果您不想要它们,请改用readTrimmedLine 方法。
【解决方案3】:

我根据Dave DeLong的源代码开了一个GitHub project。欢迎您改进代码。现在我可以前后读取文件了。

【讨论】:

    【解决方案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
    

    【讨论】:

      【解决方案5】:

      我发现GitX 也使用行阅读器。
      查看brotherbard's repository on GitHubwebsite of the Michael Stapelberg

      @Joe Yang
      好的!接下来几天我会仔细看看。
      如果你想 fork my repository on GitHub向我发送拉取请求,我会很高兴。

      【讨论】:

      • @Joe Yang:你能解释一下 readLineBackwards 是如何工作的吗?我试图整合它,但它总是返回seek to offset 0, offset in file is 0。我看不到您将偏移量移到文件末尾以向后移动。
      【解决方案6】:

      我将其重写为符合 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
      

      【讨论】:

      • 请注意,如果您将 chunkSize 从 10 更改为您正在阅读的文件中行的最大行长度,则此代码将执行得更快。就我而言,将 chunkSize 从 10 更改为 128 会使性能翻倍。
      【解决方案7】:

      这是我用来从 NSInputStream 读取单个行的方法。请注意,它针对可读性而非速度进行了优化。 ;-)

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

      【讨论】:

        【解决方案8】:

        您还可以在https://github.com/xcvista/ohttpd2/tree/master/CGIStream 上查看我为我的 HTTP 服务器项目创建的 CGIStream 库。此代码不是文件描述符,而是在 NSInputStream 上运行。它本质上是来自 Microsoft .net 框架的 System.IO.StreamReaderSystem.IO.StreamWriter 的 Objective-C 克隆。

        它不仅适用于文件,还适用于网络套接字。我用它来处理HTTP协议,与CGI前缀同名。

        【讨论】:

          【解决方案9】:

          LARGE 文本文件的这个问题的答案不需要自定义函数。 Objective-C 是 c 的超集,因此有 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 终止腾出空间。

          【讨论】:

          • 这个答案比“最佳答案”更快,代码效率更高
          • @Boris ...除非您出于某种原因必须使用 NSFileHandle,例如从 NSPipe 读取数据的情况
          【解决方案10】:

          我在其他一些情况下也遇到了类似的情况,这是我在 Swift 3 中的解决方案。假设文本文件是 utf8。

          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() */
          

          https://gist.github.com/codelynx/c1de603a85e7503fe9597d027e93f4de

          【讨论】:

            【解决方案11】:

            【讨论】:

              猜你喜欢
              • 2011-12-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-12-22
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多