2014-01-26 3 views
0

이것은 나를 혼란스럽게합니다. iOS에서 6MB CSV 파일을 한 줄씩 읽으려고합니다. 나는 일반 C 파일 포인터와 NSInputStream 폴링을 사용해 보았지만 가장 깨끗한 느낌의 다음에 정착했다. 세 가지 방법 모두 성공한 결과를 반환하는 무작위 블록처럼 보이지만 모든 null 바이트로 버퍼를 채 웁니다. 나는 "무작위"라고 말하지만 일관성이있다. 프로그램이 다시 실행될 때 읽기가 정확히 동일한 시점에서 작동하지 않으며 읽기 횟수가 의심 스럽습니다 (아래에서 더 자세히 설명합니다).비동기 큰 파일을 읽으면 iOS에서 null 읽기 블록이 발생합니다.

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

는 6 메가 바이트 파일을 읽는 동안, 두 번 나는 96의 시리즈를 얻을 수 chunk_size 인 경우 수 chunk_size가 1024 일 때 512 (192)의 일련있을 것 "나쁜 읽기" "나쁜 읽기". "나쁜 독서"란 무엇을 의미합니까? NSInputStream 읽기 메시지는 성공을 반환하며 대리자 콜백에서 오류 이벤트가 발생하지 않습니다. 그러나 bytes 버퍼는 모든 null 값을가집니다.

  • 아이폰 OS 7.0.4, 아이 패드 2
  • 함께 aprox에 파일 크기를 감소 시뮬레이터
  • 에서 발생하지 않습니다 바탕 화면
  • 에 발생하지 않습니다. 1MB는 iPad에서 문제를 "고칩니다."

메인 UI 스레드에서 리더 클래스를 인스턴스화하는 것이 가장 가치가 있습니다.

그래서 ... 나는 여기서 뭔가 미묘하게 (또는 미묘하게) 잘못하고있는 것입니까? 아니면 어떤 종류의 모호한 iOS 버그를 발견 했습니까?

+0

나는 NSLog 명령문을 추가하고, 내 버그를 찾으려고 노력하고 있습니다. 이 시점에서 심각한 문제가있는 파일 시스템의 확률은 매우 떨어진다. –

답변

0

적어도 하나의 문제는 UTF8 스트림의 임의의 청크를 읽은 다음 다시 돌아 오는 것이 일관성이 있다고 가정 할 때 발생합니다. UTF8 인코딩의 중간에서 "끊어지는"스트링의 덩어리를 얻는다면, 많은 문제가 발생할 것입니다. 부분 문자열 생성을 원한다면 알고리즘은이를 방지하기 위해 재 작업이 필요합니다.

+0

이 문제의 원인을 제거하기 위해 파일을 ASCII로 변환하여 변환되지 않은 문자를'iconv -f utf8 -t ascii -c myfile.csv> clean.csv '로 제거했습니다. 내 게시물에서 설명하는 동작은 ASCII 문자가있는 파일을 사용하는 중입니다. –

+0

왜 직렬 디스패치 큐가 아닌 NSOperation 큐를 사용하고 있습니까? NSOperation 대기열을 사용하면 여러 작업을 수행 할 수 있습니다. 현재의 솔루션은 이상하게 보입니다. 어쨌든 마지막 제안으로 파일을 Dropbox와 같은 공개 사이트에 올려 놓고 다운로드 만하는 간단한 데모 응용 프로그램을 만들어보십시오 (그러나 iPad에서는 문제가 있음). 그러면이 파일을 볼 것입니다. 50 또는 100 피트 탄력이 사람들을 동기 부여하는 데 먼 길을 간다. 나는 iPad 3 만 가지고 있기 때문에 상자 밖에서 작동하지 않을 수도 있습니다. –