Как использовать класс CHCSVParser

Я играю с Dave DeLong превосходным CHCSVParser для Objective-C с чрезвычайно длинным файлом .CSV, и я сталкиваюсь с некоторыми проблемами используй это. Я бы использовал метод arrayWithContentsOfCSVFile, но я запускаю код на iPhone и разбор всего файла в память занимает больше памяти, чем доступно.

В моем коде ниже анализатор открывает документ и отлично вызывает методы делегата, но где в делегате я останавливаюсь после каждой строки и получаю доступ к данным (для создания и сохранения объекта Core Data в хранилище данных)? Я предполагаю, что это было бы в - (void) parser:(CHCSVParser *)parser didEndLine:(NSUInteger)lineNumber, но как мне получить NSArray (или что-то другое) данных из синтаксического анализатора, когда это делается с линией?

Вот мой код:

//
// The code from a method in my view controller:
//
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSFileManager *manager = [NSFileManager defaultManager];
NSError *err = nil;
NSArray *fileList = [manager contentsOfDirectoryAtPath:documentsDirectory error:&err];
NSString *fileName = [fileList objectAtIndex:1];
NSURL *inputFileURL = [NSURL fileURLWithPath: [documentsDirectory stringByAppendingPathComponent:fileName]];


NSStringEncoding encoding = 0;
CHCSVParser *p = [[CHCSVParser alloc] initWithContentsOfCSVFile:[inputFileURL path] usedEncoding:&encoding error:nil];
[p setParserDelegate:self];
[p parse];
[p release];

...

#pragma mark -
#pragma mark CHCSVParserDelegate methods

- (void) parser:(CHCSVParser *)parser didStartDocument:(NSString *)csvFile {
    NSLog(@"Parser started!");
}

- (void) parser:(CHCSVParser *)parser didStartLine:(NSUInteger)lineNumber {
    //NSLog(@"Parser started line: %i", lineNumber);
}

- (void) parser:(CHCSVParser *)parser didEndLine:(NSUInteger)lineNumber {
    NSLog(@"Parser ended line: %i", lineNumber);
}

- (void) parser:(CHCSVParser *)parser didReadField:(NSString *)field {
    //NSLog(@"Parser didReadField: %@", field);
}

- (void) parser:(CHCSVParser *)parser didEndDocument:(NSString *)csvFile {
    NSLog(@"Parser ended document: %@", csvFile);
}

- (void) parser:(CHCSVParser *)parser didFailWithError:(NSError *)error {
    NSLog(@"Parser failed with error: %@ %@", [error localizedDescription], [error userInfo]);
}

Спасибо!

Ответы

Ответ 1

Я рад видеть, что мой код оказался полезным!:)

CHCSVParser похож по поведению на NSXMLParser, поскольку каждый раз, когда он находит что-то интересное, он будет сообщать вам через один из обратных вызовов делегата. Однако, если вы решите игнорировать данные, которые он дает вам в обратном вызове, он исчез. Эти синтаксические анализаторы (CHCSVParser и NSXMLParser) довольно глупы. Они просто знают формат материала, который они пытаются разобрать, но на самом деле не делают этого намного дальше.

Итак, ответ в двух словах - "вы должны сами его сохранить". Если вы посмотрите на код категории NSArray, в файле .m вы увидите, что он использует простой подкласс NSObject в качестве делегата парсера, и этот подкласс - это то, что агрегирует поля в массив, а затем добавляет этот массив в общий массив. Вам нужно будет сделать что-то подобное.

Пример делегата:

@interface CSVParserDelegate : NSObject <CHCSVParserDelegate> {
  NSMutableArray * currentRow;
}
@end

@implementation CSVParserDelegate

- (void) parser:(CHCSVParser *)parser didStartLine:(NSUInteger)lineNumber {
  currentRow = [[NSMutableArray alloc] init];
}
- (void) parser:(CHCSVParser *)parser didReadField:(NSString *)field {
  [currentRow addObject:field];
}
- (void) parser:(CHCSVParser *)parser didEndLine:(NSUInteger)lineNumber {
  NSLog(@"finished line! %@", currentRow);
  [self doSomethingWithLine:currentRow];
  [currentRow release], currentRow = nil;
}
@end

Тем не менее, я мог бы убедить меня изменить поведение анализатора для агрегации самой строки, но если я схожу с этого маршрута, почему бы не просто агрегировать весь файл целиком? (Ответ: он не должен)

Ответ 2

Я попытался использовать это сегодня, основываясь на превосходном ответе и коде на @DaveDeLong, но я думаю, что программное обеспечение было пересмотрено с момента его ответа (2010). На момент написания статьи я обнаружил, что должен был использовать это:

@interface CSVParserDelegate : NSObject <CHCSVParserDelegate> {
  NSMutableArray * currentRow;
}
@end

@implementation CSVParserDelegate

- (void) parser:(CHCSVParser *)parser didBeginLine:(NSUInteger)lineNumber {
  currentRow = [[NSMutableArray alloc] init];
}
- (void) parser:(CHCSVParser *)parser didReadField:(NSString *)field atIndex:(NSInteger)fieldIndex {
  [currentRow addObject:field];
}
- (void) parser:(CHCSVParser *)parser didEndLine:(NSUInteger)lineNumber {
  NSLog(@"finished line! %@", currentRow);
  [self doSomethingWithLine:currentRow];
  [currentRow release], currentRow = nil;
}
@end

i.e., parser:didStartLine:lineNumber: стало parser:didBeginLine:lineNumber:, а parser:didReadField: стало parser:didReadField:atIndex:.