+ (NSMutableArray*)readLine:(NSString*)fileLine {
NSMutableArray* partsData = [[NSMutableArray alloc] init];
NSMutableArray* phrase = [[NSMutableArray alloc] init];
NSArray* tempPartsData = [fileLine componentsSeparatedByString:@","];
for (NSString* origPart in tempPartsData) {
NSString* part = [[NSString alloc] initWithString:[origPart stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
if ([part length] == 0) {
[part release];
continue;
}
if ([part characterAtIndex:0] == '"' && [part characterAtIndex:[part length]-1] != '"') {
// Starting phrase
[phrase addObject:[part substringFromIndex:1]];
} else if ([part characterAtIndex:0] == '"' && [part length] == 1) {
// Just a lonely quotation mark--a phrase started or ended with a comma
[phrase addObject:@""];
if ([phrase count] == 0) {
// phrase begins with a comma
} else {
// phrase ends with a comma
[partsData addObject:[phrase componentsJoinedByString:@","]];
[phrase removeAllObjects];
}
} else if ([part characterAtIndex:0] == '"' && [part characterAtIndex:[part length]-1] == '"') {
// Complete quoted phrase
NSRange inner;
inner.location = 1;
inner.length = [part length] - 2;
[partsData addObject:[part substringWithRange:inner]];
} else if ([part characterAtIndex:[part length]-1] == '"') {
// Ending phrase
[phrase addObject:[part substringToIndex:[part length]-1]];
[partsData addObject:[phrase componentsJoinedByString:@","]];
[phrase removeAllObjects];
} else if ([phrase count] > 0) {
// Continuing phrase
[phrase addObject:part];
} else {
// Complete phrase
[partsData addObject:part];
}
[part release];
}
[phrase removeAllObjects];
[phrase release];
return [partsData autorelease];
}
Thanks for this. Very simple, and seems to be working so far, but I am not done with my implementation of it yet.
ReplyDeleteI have run into one problem. If a value in the csv ends in a comma (ex:"this value","the next value,","another value"), then it breaks.
I think I have it working now.
ReplyDelete/***** old code *****/
if ([part characterAtIndex:0] == '"' && [part characterAtIndex:[part length]-1] != '"') {
// Starting phrase
[phrase addObject:[part substringFromIndex:1]];
} else if ([part characterAtIndex:0] == '"' && [part characterAtIndex:[part length]-1] == '"') {
// Complete quoted phrase
/***** new code *****/
if ([part characterAtIndex:0] == '"' && [part characterAtIndex:[part length]-1] != '"') {
// Starting phrase
[phrase addObject:[part substringFromIndex:1]];
} else if ([part characterAtIndex:0] == '"' && [part length] == 1) {
// just a lonely quotation mark - that means a phrase started or ended with a comma
// add an empty string to the phrase array - it will be turned into a comma when the phrase parts are joined
[phrase addObject:@""];
if ([phrase count] == 0) {
// phrase begins with a comma
} else {
// phrase ends with a comma
[partsData addObject:[phrase componentsJoinedByString:@","]];
[phrase removeAllObjects];
}
} else if ([part characterAtIndex:0] == '"' && [part characterAtIndex:[part length]-1] == '"') {
// Complete quoted phrase
Thanks again!
I second what Rich said - Very useful, Thanks!
ReplyDeleteOne more small problem. If you try to use this on a text stream that has embedded linefeeds at EOL it will parse the first and last csv value together into one string. Assuming you want the text stream parsed into text lines where the linefeed ends the last character, you can modify the line:
NSArray* tempPartsData = [fileLine componentsSeparatedByString:@","];
to this:
NSArray* tempPartsData = [fileLine componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@",\n"]];
using componentsSeparatedByCharactersInSet and adding linefeed to the NSCharacterSet string.
Most people probably would use it as written, but this makes it handy if you need to parse a long text stream.
Just my 2 cents... Thanks again for a great post!
Thank you very much for this.
ReplyDeletethe only problem i'm having is it doesn't parse this kind of line properly:
"1,2345 - 49,666","20% of the amount 2,000"
Hi guys I can't get this code works, does someone has a sample on how to use it ?
ReplyDeletePut this class method in your CSV class (i.e. CsvReader) and call it like this:
ReplyDeleteNSString *line = my comma delimited string
NSMutableArray *parts = [CsvReader readLine:line];
You do not need to release the 'parts' array.
Hi..Thanks for your post. However, your code seems not working. Maybe UTF8 character set is needed. If possible, please do update using UTF8 character set.
ReplyDelete