Tuesday, November 10, 2009

iPhone / Objective-C reading .csv files

I've been working on an iPhone application where I needed to save some data into the SQLite database via CoreData. The data I had was in a .csv file and I needed to preload this data on to the phone. I had two choices:
  1. Create an SQLite database by some programmatic means and import this as the store for CoreData.
  2. Read the .csv file and create NSManagedObject instances which can be saved to the context.
Option 1 seemed a little more daunting for me, so I chose the second option using NSUserDefaults to save a flag that I'd already read the .csv. I'm still investigating the first option and will report back if I'm successful. Anyway, here's a sample of the contents of the file:
  1. 31939,-37.838596,144.96726,1 - abcd
  2. 31752,-37.856249,144.979277,2 - efgh
  3. 31751,-37.854868,144.978053,3 - ijkl
  4. 31750,-37.856094,144.977565,4 mnop
  5. 31749,-37.855388,144.9753,4 qrst
So that's an excerpt of the .csv - four columns of integer, double, double and a string. Including your csv file in the resources folder of your XCode project. Ensure that the "Target" checkbox is checked (Its on the top of the right content pane when the .csv file has been selected). Next is the code to load this data:
  1. //
  2. // load the points from our local resource
  3. //
  4. NSString* filePath = [[NSBundle mainBundle] pathForResource:@"a_csv_file" ofType:@"csv"];
  5. NSString* fileContents = [NSString stringWithContentsOfFile:filePath];
  6. NSArray* pointStrings = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
  7. //NSMutableArray* points = [[NSMutableArray alloc] initWithCapacity:pointStrings.count];
  8. for(int idx = 0; idx < pointStrings.count; idx++)
  9. {
  10. // break the string down even further to the columns
  11. NSString* currentPointString = [pointStrings objectAtIndex:idx];
  12. NSArray* arr = [currentPointString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@","]];
  13. NSNumber *colTwo = [[NSNumber alloc] initWithDouble: [[arr objectAtIndex:1] doubleValue]];
  14. NSNumber *colThree = [[NSNumber alloc] initWithDouble: [[arr objectAtIndex:2] doubleValue]];
  15. NSNumber *colOne = [[NSNumber alloc] initWithInt:[[arr objectAtIndex:0] intValue]];
  16. NSString *colFour = [[NSString alloc] initWithFormat:@"%@", [arr objectAtIndex:3]];
  17. //... we can now use these pointers to save to the CoreData database
  18. }
In line 06 of the code above, we've selected a newLineCharacterSet as the delimiter so that the file is split by lines. In line 14, we then use the comma character set to split the columns by comma. Et Voila! Finally, see the rather odd way of reading strings. Must be a simpler way...

1 comment:

Kevin Branigan said...

This will work in most cases - but you'll find the CSV standard allows for embedded new lines in content which is quoted. Though it's a fault I'm willing to accept as my data, like yours, doesn't have any text fields.