As simple as possible: I have a very simple Excel spreadsheet with just over 1k records. I want to use this as a static datasource for an iPhone application.
What's the best plan of attack?
Possibilities in my mind:
1) Reading the XLS directly as a data source: is there an Obj-C lib for this?
2) Converting the XLS to a format that Obj-C has a lib for... CSV? XML? some native CoreData format?
3) Importing it into something MySQLish and just getting an XML feed from a server.
I would need some help figuring these approaches out. I've never worked with Excel.
1 would be nice, 2 would be probably the best solution for what I am doing right now, and 3 I pretty much know how to do but I am not actually sure if MySQL has an XLS import (I believe MSSQL does).
I had a similar problem at one point. What I ended up doing was writing a simple application that took a CSV file and converted it into a Plist. Then I used the Plist as needed in the app. This code uses cCSVParse. It will use the headers in the first row as the key names to create an array of dictionaries for each successive row. The output is a tidy plist file with all of your data. Use [NSArray arrayWithContentsOfFile:] to pop the data into memory in your app.
CSVParser *parser = [CSVParser new];
[parser openFileWithPath:pathAsString];
NSMutableArray *csvContent = [parser parseFile];
[parser closeFile];
if (pathAsString != nil)
{
NSArray *keyArray = [csvContent objectAtIndex:0];
NSMutableArray *plistOutputArray = [NSMutableArray array];
NSInteger i = 0;
for (NSArray *array in csvContent)
{
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
NSInteger keyNumber = 0;
for (NSString *string in array)
{
[dictionary setObject:string forKey:[keyArray objectAtIndex:keyNumber]];
keyNumber++;
}
if (i > 0)
{
[plistOutputArray addObject:dictionary];
}
i++;
}
NSMutableString *mutableString = [NSMutableString stringWithString:pathAsString];
[mutableString replaceOccurrencesOfString:#".csv" withString:#".plist" options:nil range:NSMakeRange([mutableString length]-4, 4)];
NSURL *url = [NSURL fileURLWithPath:mutableString];
[plistOutputArray writeToURL:url atomically:YES];
I also built a pretty simple UI for this. Maybe I'll clean up the whole project and post it on Google Code.
You might look into rendering the Excel file with a UIWebView instance, and seeing if you can access the DOM within the web view.
If you can access the DOM, you could perhaps use libxml2 or a similar library to write a parser that retrieves data from it.
Related
I'm using Youtube API, I'd like to have a search auto-complete feature, just like int the site, when you type into the search input box for iPhone App, it gives you terms suggestions. I've read the docs, but still missing, Is this possible using the API?
Well, i know it's too late to answer here, but i will post this answer because it's something that drove me crazy for a couple of days!!! and hope it will save to others...
So... i'm using this API :
http://suggestqueries.google.com/complete/search?client=youtube&ds=yt&alt=json&q=%#
(q is the query for the autocomplete search).
Now, if you try to open a browser, paste this API and change q=%# to (lets say): q=br, you will notice that some file with the suffix .js is downloaded to your computer.
For some reason, i couldn't parse the JSON like that, so i did that trick:
#property(strong, nonatomic) NSMutableArray *ParsingArray // Put that in .h file or after #interface in your .m file
-(void)autocompleteSegesstions : (NSString *)searchWish{
//searchWish is the text from your search bar (self.searchBar.text)
NSString *jsonString = [NSString stringWithFormat:#"http://suggestqueries.google.com/complete/search?client=youtube&ds=yt&alt=json&q=%#", searchWish];
NSString *URLString = [jsonString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; // Encoding to identify where, for example, there are spaces in your query.
NSLog(#"%#", URLString);
NSData *allVideosData = [[NSData alloc]initWithContentsOfURL:[[NSURL alloc]initWithString:URLString]];
NSString *str = [[NSString alloc]initWithData:allVideosData encoding:NSUTF8StringEncoding];
NSLog(#"%#", str); //Now you have NSString contain JSON.
NSString *json = nil;
NSScanner *scanner = [NSScanner scannerWithString:str];
[scanner scanUpToString:#"[[" intoString:NULL]; // Scan to where the JSON begins
[scanner scanUpToString:#"]]" intoString:&json];
//The idea is to identify where the "real" JSON begins and ends.
json = [NSString stringWithFormat:#"%#%#", json, #"]]"];
NSLog(#"json = %#", json);
NSArray *jsonObject = [NSJSONSerialization JSONObjectWithData:[json dataUsingEncoding:NSUTF8StringEncoding] //Push all the JSON autocomplete detail in to jsonObject array.
options:0 error:NULL];
self.ParsingArray = [[NSMutableArray alloc]init]; //array that contains the objects.
for (int i=0; i != [jsonObject count]; i++) {
for (int j=0; j != 1; j++) {
NSLog(#"%#", [[jsonObject objectAtIndex:i] objectAtIndex:j]);
[self.ParsingArray addObject:[[jsonObject objectAtIndex:i] objectAtIndex:j]];
//Parse the JSON here...
}
}}
That's it. now ParsingArray is the array that contains all autocomplete information from youTube! to be able to change it every time the user clicks another character on the searchBar, use this function:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
[self autocompleteSegesstions:self.searchBar.text];}
Now, this is the main code you should have. to make this code faster (because you can now see that you have a writing delay on the keyboard), use another thread to download ParsingArray or use Asynchronous block. (just insert the content of the first method to Async block...)
Remember- maybe there is another way to implement autocomplete youTube search much better then this, but i just did not found it, and i searched a lot! if anyone know a better way, i'll be more then glad if he post it here.
Have fun!!!
Not the Youtube API -- but you can use the Google Suggest API. Make calls to this URL:
http://suggestqueries.google.com/complete/search?client=youtube&ds=yt&q=QUERY
Which will return a json response of the suggest terms that your app can parse and display. If you prefer XML to json, change
client=youtube
to
output=toolbar
(leave the rest of the parameters the same).
I'm looking at adding a distance calculator to my application. I have been looking at Google's API put i cant seem to decode the JSON. I have managed to do so with PHP. The code for that was:
substr($convertedtoarray['routes']['0']['legs']['0']['distance']['text'], 0, -3);
On the iPhone i managed to get the JSON response but can't get the specific part of it that I want.
Json address: http://maps.googleapis.com/maps/api/directions/json?origin=plymouth&destination=pl210bp&sensor=false
NSMutableDictionary *luckyNumbers = [responseString JSONValue];
[responseString release];
if (luckyNumbers != nil) {
NSString *responseStatus = [luckyNumbers objectForKey:#"routes"];'
}
Where would I go from here?
Any help would be great cheers
NSString *responseStatus = [[[[[[luckyNumbers objectForKey:#"routes"]objectAtIndex:0] objectForKey:#"legs"]objectAtIndex:0] objectForKey:#"distance"] objectForKey:#"text"];
Very ugly you can extract in separate objects like this:
NSArray *routesArray = [luckyNumbers objectForKey:#"routes"];
NSDictionary *firstRoute = [routesArray objectAtIndex:0];
NSArray *legsArray = [firstRoute objectForKey:#"legs"];
NSDictionary *firstLeg = [legsArray objectAtIndex:0];
NSDictionary *distanceDict = [firstLeg objectForKey:#"distance"];
NSString *distanceText = [distanceDict objectForKey:#"text"];
Good luck.
The easiest thing to do would be to create a dictionary iterator, and loop over what children the luckynumbers dictionary has, you can print out, or debug, to see what the keys for these children are, and what object types they are.
I used this technique a few times to figure out what the structure of an XML doc I was being returned was like.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking us to recommend or find a tool, library or favorite off-site resource are off-topic for Stack Overflow as they tend to attract opinionated answers and spam. Instead, describe the problem and what has been done so far to solve it.
Closed 9 years ago.
Improve this question
I'm looking for an easy to use CSV parser for Objective-C to use on the iPhone. Where can I find one?
I'm also looking for other parsers such as JSON, so maybe there is a conversion library somewhere.
I finally got around to cleaning up a parser I've had in my code folder and posted it on Github: http://github.com/davedelong/CHCSVParser
It's quite thorough. It handles all sorts of escaping schemes, newlines in fields, comments, etc. It also uses intelligent file loading, which means you can safely parse huge files in constrained memory conditions.
Here's a simple category on NSString to parse a CSV string that has commas embedded inside quote blocks.
#import "NSString+CSV.h"
#implementation NSString (CSV)
- (NSArray *)componentsSeparatedByComma
{
BOOL insideQuote = NO;
NSMutableArray *results = [[NSMutableArray alloc] init];
NSMutableArray *tmp = [[NSMutableArray alloc] init];
for (NSString *s in [self componentsSeparatedByString:#","]) {
if ([s rangeOfString:#"\""].location == NSNotFound) {
if (insideQuote) {
[tmp addObject:s];
} else {
[results addObject:s];
}
} else {
if (insideQuote) {
insideQuote = NO;
[tmp addObject:s];
[results addObject:[tmp componentsJoinedByString:#","]];
tmp = nil;
tmp = [[NSMutableArray alloc] init];
} else {
insideQuote = YES;
[tmp addObject:s];
}
}
}
return results;
}
#end
This assumes you've read your CSV file into an array already:
myArray = [myData componentsSeparatedByString:#"\n"];
The code doesn't account for escaped quotes, but it could easily be extended to.
Quick way to do this:
NSString *dataStr = [NSString stringWithContentsOfFile:#"example.csv" encoding:NSUTF8StringEncoding error:nil];
NSArray *array = [dataStr componentsSeparatedByString: #","];
Well, above simple solutions doesn't take into account multiple records.
Use the following code reading a default excel CSV using ASCI 13 as line end marker:
NSString *content = [NSString stringWithContentsOfFile:filepath encoding:NSUTF8StringEncoding error:nil];
NSArray *contentArray = [content componentsSeparatedByString:#"\r"]; // CSV ends with ACSI 13 CR (if stored on a Mac Excel 2008)
for (NSString *item in contentArray) {
NSArray *itemArray = [item componentsSeparatedByString:#";"];
// log first item
NSLog(#"%#",[itemArray objectAtIndex:0]);
}
I wrote a dead-simple (although not fully-featured) CSV parser for a project I was working on: CSVFile.h and CSVFile.m. Feel free to grab it -- the code is available under the GPLv3 (unfortunately, it was a requirement for the project I was working on) but I'd be happy to license it to you under an MIT license or another license.
This seems to be the most comprehensive that I've found so far.
http://www.macresearch.org/cocoa-scientists-part-xxvi-parsing-csv-data
As a side note, you'd think most major languages (Delphi, C#, Objective-c, php etc) would have a library available with a full implementation of this basic data interchange format.
I know json is cool and XML is reliable but neither are available as a save option from most applications saving table data. CSV still is.
I've found ParseKit few weeks ago
But IMHO for most cases -[NSString componentsSeparatedByString:] method and NSScanner are more than enough and quite easy to use.
As xmr said above: It's possible in Objective C to convert an NSString csv into 'components separated by string' array.
NSArray* items;
items=[bufferString componentsSeparatedByString:#","];
In case you are interested in csv export having arrived at this thread - as I did - here is an extract of how I exported a csv file.
NSString* fileName = #"Level";
fileName = [fileName stringByAppendingString:levelNumberBeingEdited];
fileName = [fileName stringByAppendingString:#".txt"];
NSString* bufferString=#"";
Buffer String is populated by looping through each data item (not shown) and inserting a comma between each. Finally it's exported.
NSString* homeDir = NSHomeDirectory();
NSString* fullPath = [homeDir stringByAppendingPathComponent:fileName];
NSError* error = nil;
[bufferString writeToFile:fullPath atomically:NO encoding:NSASCIIStringEncoding error:&error];
In my iPhone app, I have two plist files to store "Themes". One is a read-only file containing default themes, and one contains custom Themes that the user has created. I'm using plist files because it's very easy for me to read from the plist and create new Theme objects.
My plist is an array of dictionary objects.
Is there any easy way to append a new dictionary object to my plist file? Or do I need to read the file into memory, append the new dictionary object, and write it back to the filesystem?
Thanks!
With Cocoa, you need to read the file into memory, append the new dictionary object, and write it back to the filesystem. If you use an XML plist, you could pretty easily parse it and incrementally write to the file, but it'd also be quite a bit bigger, so it's unlikely to be worth it.
If rewriting the plist is taking too long, you should investigate using a database instead (perhaps via Core Data). Unless the file is huge, I doubt this will be an issue even with the iPhone's memory capacity and flash write speed.
(I copied this for those who don't want to click a link from a similar question I answered here: A question on how to Get data from plist & how should it be layout)
Here are two methods to read and write values from a plist using an NSDictionary:
- (NSMutableDictionary*)dictionaryFromPlist {
NSString *filePath = #"myPlist.plist";
NSMutableDictionary* propertyListValues = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
return [propertyListValues autorelease];
}
- (BOOL)writeDictionaryToPlist:(NSDictionary*)plistDict{
NSString *filePath = #"myPlist.plist";
BOOL result = [plistDict writeToFile:filePath atomically:YES];
return result;
}
and then in your code block somewhere:
// Read key from plist dictionary
NSDictionary *dict = [self dictionaryFromPlist];
NSString *valueToPrint = [dict objectForKey:#"Executable file"];
NSLog(#"valueToPrint: %#", valueToPrint);
// Write key to plist dictionary
NSString *key = #"Icon File";
NSString *value = #"appIcon.png";
[dict setValue:value forKey:key];
// Write new plist to file using dictionary
[self writeDictionaryToPlist:dict];
This is how I am appending data to the plist:
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
[array addObject:countdownLabel.text];
[array writeToFile:[self dataFilePath] atomically:YES];
[array release];
}
else
{
NSArray *array = [NSArray arrayWithObject:countdownLabel.text];
[array writeToFile:filePath atomically:YES];
}
I have a pretty straightforward Excel spreadsheet, and I need to use the data in an iPhone app. The xls document has 6 columns, and > 200 rows.
I would like to create a plist from the xls document. How can I convert one to the other, programmatically?
I'm late to the party but I built a desktop utility that will convert CSV to a plist. You can download the binary or use this code, which requires cCSVParse. It uses whatever is in row 0 to create key names, then generates dictionaries for each successive row.
CSVParser *parser = [CSVParser new];
[parser openFileWithPath:pathAsString];
NSMutableArray *csvContent = [parser parseFile];
[parser closeFile];
if (pathAsString != nil)
{
NSArray *keyArray = [csvContent objectAtIndex:0];
NSMutableArray *plistOutputArray = [NSMutableArray array];
NSInteger i = 0;
for (NSArray *array in csvContent)
{
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
NSInteger keyNumber = 0;
for (NSString *string in array)
{
[dictionary setObject:string forKey:[keyArray objectAtIndex:keyNumber]];
keyNumber++;
}
if (i > 0)
{
[plistOutputArray addObject:dictionary];
}
i++;
}
NSMutableString *mutableString = [NSMutableString stringWithString:pathAsString];
[mutableString replaceOccurrencesOfString:#".csv" withString:#".plist" options:nil range:NSMakeRange([mutableString length]-4, 4)];
NSURL *url = [NSURL fileURLWithPath:mutableString];
[plistOutputArray writeToURL:url atomically:YES];
You could do this using a simple formula that you copy and pasted down a column beside each of your 200+ rows.
For example, assuming colum A contains a list of names, and column B contains a matching set of ages you could use a formula such as the following to end up with most of the XML for a plist based dictionary.
=CONCATENATE("<key>Name</key><string>", A1,"</string><key>Age</key><integer>",B1,"</integer>")
You then select all the cells within this new column you can copy and paste into notepad or another text editor to save it as a plist file (you may want to put some hardcoded text into a cell above and below your 200+ rows, in order to get the required tags etc as well...
Ladies and gentlemen,
I tried any other recommended solutions above but because of Unicode characters in my language (Turkish) none of them worked out for me... All unicode characters were all broken. Then I decided to make a tool for this.
I proudly present the simplest way to convert any XLS or XLSX or CVS file to a plist:
http://exceltoplist.herokuapp.com/
Just upload your XLS, XLSX or CSV and download your Apple Plist!
Enjoy!
Note: Because of Heroku's free dyno policy it might take a few moments to browse the page. Just keep waiting for 5-10 seconds to open page.
For OpenOffice, use this formula
=CONCATENATE("<key>number</key><integer>"; A2;"</integer><key>MyString</key><string>";B2;"</string>")
I found the CONCATENATE to work the best for this.
For my purpose I just need to convert CSV with two columns to plist file.
First column is keys and second are values. So, I slightly change Danilo Campos code as following:
CSVParser *parser = [CSVParser new];
[parser openFileWithPath:pathAsString];
NSMutableArray *csvContent = [parser parseFile];
[parser closeFile];
if (pathAsString != nil)
{
NSMutableDictionary *plistOutputArray = [NSMutableDictionary dictionary];
for (NSArray *array in csvContent)
{
NSString *key = (NSString *)([array objectAtIndex:0]);
NSString *value = (NSString *)([array objectAtIndex:1]);
[plistOutputArray setObject:value forKey:key];
}
NSMutableString *mutableString = [NSMutableString stringWithString:pathAsString];
[mutableString replaceOccurrencesOfString:#".csv" withString:#".plist" options:nil range:NSMakeRange([mutableString length]-4, 4)];
NSURL *url = [NSURL fileURLWithPath:mutableString];
[plistOutputArray writeToURL:url atomically:YES];
}
P.S. You can find his initial source code here - http://code.google.com/p/danilobits/source/checkout
Please note that to get his code work now you need to change "Base SDK" to "Latest OS X"
Use http://shancarter.github.io/mr-data-converter/ to convert xls to a Json(just copy & paste)(can re format it by remove white space in http://jsonviewer.stack.hu/). save json to text file named: in.json.
Use plutil command to format json to plist
plutil -convert xml1 in.json -o out.plist