Как использовать класс 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:
.