Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
487 views
in Technique[技术] by (71.8m points)

iphone - How To Use the CHCSVParser class

I am playing around with Dave DeLong's excellent CHCSVParser for Objective-C with an extremely long .CSV file and am running into some trouble using it. I would use the arrayWithContentsOfCSVFile method, but I'm running the code on an iPhone and parsing the whole file into memory would take more memory than is available.

In my code below, the parser opens the document and calls the delegate methods perfectly, but where in the delegate do I stop after each line and access the data (to create and save a Core Data object to the data store)? I assume that would be in - (void) parser:(CHCSVParser *)parser didEndLine:(NSUInteger)lineNumber, but how do I get an NSArray (or whatever) of the data from the parser when it's done with the line?

Here is my code so far:

//
// 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]);
}

Thanks!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I'm glad to see that my code is proving useful! :)

CHCSVParser is similar in behavior to an NSXMLParser, in that every time it finds something interesting, it's going to let you know via one of the delegate callbacks. However, if you choose to ignore the data that it gives you in the callback, then it's gone. These parsers (CHCSVParser and NSXMLParser) are pretty stupid. They just know the format of the stuff they're trying to parse, but don't really do much beyond that.

So the answer, in a nutshell, is "you have to save it yourself". If you look at the code for the NSArray category, you'll see in the .m file that it's using a simple NSObject subclass as the parser delegate, and that subclass is what's aggregating the fields into an array, and then adding that array to the overall array. You'll need to do something similar.

Example delegate:

@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

However, I could be convinced to modify the behavior of the parser to aggregate the row itself, but if I go down that route, why not just have the parser aggregate the entire file? (Answer: it shouldn't)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...