2014-01-26 69 views
0

這讓我難堪。我正在嘗試在iOS上逐行閱讀6MB CSV文件。我已經嘗試使用純C文件指針和NSInputStream輪詢,但在下面感覺最乾淨的定位。所有這三種方法都會導致看起來像讀取成功的隨機塊,但會用全部空字節填充緩衝區。我說「隨機」,但它具有一致性。在重新運行程序時,讀取在完全相同的時刻停止工作,並且讀取次數可疑(下面會詳細介紹)。異步讀取大文件導致iOS中的空塊讀取

- (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文件,兩次我會得到一系列96「壞寫着」當CHUNK_SIZE是1024。如果CHUNK_SIZE是512將會有一系列192「不好讀」。 「壞讀」是什麼意思? NSInputStream讀取消息返回成功,並且委託回調中沒有發生錯誤事件。然而,bytes緩衝區具有全部空值。

  • 的iOS 7.0.4,iPad 2的
  • 不上桌面
  • 發生在模擬器
  • 不會發生減少文件大小aprox的。 1MB「修復」在iPad上的問題

最有可能值得注意的是,我在主UI線程上實例化reader類。

所以......我在這裏微妙地(或不是微妙地)做錯了什麼?還是我發現了一些不明顯的iOS錯誤?

+0

我想添加一些NSLog語句,並通過它們試圖找到我的錯誤。此時存在嚴重問題的文件系統的可能性非常小。 –

回答

0

至少有一個問題是你正在讀取UTF8流的隨機塊,然後假設你得到的是一致的。如果你在UTF8編碼中得到一個「破碎」的字符串,會導致一系列問題。如果你想做部分字符串構造,你的算法將需要返工以防止這種情況 - 這不是一件小事。

+0

爲了消除這個問題,我將這個文件轉換爲ascii,用'iconv -f utf8 -t ascii -c myfile.csv> clean.csv'去除任何非convertalbe字符。我在文章中描述的行爲是在使用帶有ascii字符的文件時。 –

+0

爲什麼你使用NSOperation隊列而不僅僅是一個串行調度隊列? NSOperation隊列允許發生多個操作。你目前的解決方案似乎很奇怪。無論如何,作爲最後一個建議,將文件放在像Dropbox這樣的公共站點上,啓動一個簡單的演示應用程序,只需下載(但在iPad上有問題),然後我會查看它(也許別人也會)。在這個網站上建立積分是值得的,因爲50或100點的彈性將會激勵人們很長的路。我只有一臺iPad 3,因此它可能會或可能無法使用。 –