0

这让我很难过。我正在尝试逐行读取 iOS 上的 6MB CSV 文件。我尝试过使用纯 C 文件指针和 NSInputStream 轮询,但选择了以下感觉最干净的方法。所有这三种方法都会导致看似随机的读取块返回成功,但会用所有空字节填充缓冲区。我说“随机”,但它具有一致性。重新运行程序时,读取在完全相同的点停止工作,并且读取的数量是可疑的(更多内容见下文)。

- (id)initWithFileAtPath:(NSString *)path {
   if ((self = [super init])) {
      filePath = [path copy];
      queue = [[NSOperationQueue alloc] init];
      queue.maxConcurrentOperationCount = 1;
      buffer = [[NSMutableString alloc] init];
      bytes = malloc(CHUNK_SIZE * sizeof(UTF8Char));
   }

   return self;
}

- (void)dealloc {
   [filePath release];
   [queue release];
   [buffer release];
   free(bytes);
   [super dealloc];
}

- (void)stream:(NSInputStream *)stream handleEvent:(NSStreamEvent)eventCode {
   switch (eventCode) {
      case NSStreamEventOpenCompleted:
         break;
      case NSStreamEventHasBytesAvailable:
         [queue addOperationWithBlock:^{
            [self readChunk: stream];
            [self drainBuffer];
         }];
         break;
      case NSStreamEventEndEncountered:
         if ([buffer length] > 0) {
            [delegate reader:self didReadLine:[NSString stringWithString:buffer]];
            [buffer setString:@""];
         }

         [stream close];
         [stream removeFromRunLoop:[NSRunLoop currentRunLoop]
                           forMode:NSDefaultRunLoopMode];

         [stream release];

         [delegate readerDidFinishReading:self];

         break;
      default:
         NSLog(@"StreamReader: event %d", eventCode);
         break;
   }
}

- (void)enumerateLines {
   NSInputStream *stream = [[NSInputStream alloc] initWithFileAtPath:filePath];
   stream.delegate = self;

   [stream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                     forMode:NSDefaultRunLoopMode];

   [stream open];
}

- (void)readChunk: (NSInputStream*)stream {
   NSInteger readSize = [stream read:bytes maxLength:CHUNK_SIZE];
   if (readSize) {
      if (bytes[0] == '\0') {
         NSLog(@"null buffer %d", readSize);
      }
      NSString *string = [[NSString alloc] initWithBytes:bytes
                                                  length:readSize
                                                encoding:NSUTF8StringEncoding];
      [buffer appendString:string];
      [string release];
   } else {
      NSLog(@"StreamReader: read zero bytes");
   }
}

- (void)drainBuffer {
   static NSCharacterSet *newlines = nil;
   if (newlines == nil) {
      newlines = [NSCharacterSet newlineCharacterSet];
   }

   NSRange newlinePos;
   while ((newlinePos = [buffer rangeOfCharacterFromSet:newlines]).location != NSNotFound) {
      NSString *line = [buffer substringToIndex:newlinePos.location];

      // remove the line from the buffer along with line separator
      [buffer deleteCharactersInRange: (NSRange){0, [line length]}];
      while ([buffer length] > 0 && [newlines characterIsMember:[buffer characterAtIndex:0]]) {
         [buffer deleteCharactersInRange:(NSRange){0, 1}];
      }

      [delegate reader:self didReadLine: line];
   }
}

在读取 6MB 文件时,当 CHUNK_SIZE 为 1024 时,我将获得一系列 96 次“错误读取”。如果 CHUNK_SIZE 为 512,则会出现一系列 192 次“错误读取”。我所说的“坏读”是什么意思?NSInputStream 读取消息返回成功,委托回调中没有错误事件发生。然而bytes缓冲区具有所有空值。

  • iOS 7.0.4,iPad 2
  • 不会发生在桌面上
  • 在模拟器中不会发生
  • 将文件大小减小到大约。1MB“修复”了 iPad 上的问题

最有可能值得注意的是,我在主 UI 线程上实例化了 reader 类。

所以......我在这里做错了什么微妙(或不微妙)吗?还是我发现了某种晦涩难懂的 iOS 错误?

4

1 回答 1

0

至少一个问题是您正在读取 UTF8 流的随机块,然后假设您返回的内容是连贯的。如果你在 UTF8 编码的中间得到一个“中断”的字符串,它会导致一系列问题。如果您想进行部分字符串构造,您的算法将需要返工以防止这种情况发生 - 这不是一件容易的事。

于 2014-01-26T23:04:41.597 回答