views:

615

answers:

3

In my application i need to export some data into CSV or html format. How can i do this. Any help will be greatly appreciated.

A: 

CSV - comma separated values.

I usually just iterate over the data structures in my application and output one set of values per line, values within set separated with comma.

struct person
{
    string first_name;
    string second_name;
};

person tony = {"tony", "momo"};
person john = {"john", "smith"};

would look like

tony, momo
john, smith
stefanB
That doesn't actually show how to go from the record to the string.
Peter Hosey
+1  A: 

RegexKitLite comes with an example of how to read a csv file into an NSArray of NSArrays, and to go in the reverse direction is pretty trivial.

It'd be something like this (warning: code typed in browser):

NSArray * data = ...; //An NSArray of NSArrays of NSStrings
NSMutableString * csv = [NSMutableString string];
for (NSArray * line in data) {
  NSMutableArray * formattedLine = [NSMutableArray array];
  for (NSString * field in line) {
    BOOL shouldQuote = NO;
    NSRange r = [field rangeOfString:@","];
    //fields that contain a , must be quoted
    if (r.location != NSNotFound) {
      shouldQuote = YES;
    }
    r = [field rangeOfString:@"\""];
    //fields that contain a " must have them escaped to "" and be quoted
    if (r.location != NSNotFound) {
      field = [field stringByReplacingOccurrencesOfString:@"\"" withString:@"\"\""];
      shouldQuote = YES;
    }
    if (shouldQuote == YES) {
      [formattedLine addObject:[NSString stringWithFormat:@"\"%@\"", field]];
    } else {
      [formattedLine addObject:field];
    }
  }
  NSString * combinedLine = [formattedLine componentsJoinedByString:@","];
  [csv appendFormat:@"%@\n", combinedLine];
}
[csv writeToFile:@"/path/to/file.csv" atomically:NO];
Dave DeLong
ok.How can i export to html file ?
diana
@Edwin HTML is just a text format, so build the string and write it to a file. We can't write your app for you...
Dave DeLong
+2  A: 

The general solution is to use stringWithFormat: to format each row. Presumably, you're writing this to a file or socket, in which case you would write a data representation of each string (see dataUsingEncoding:) to the file handle as you create it.

If you're formatting a lot of rows, you may want to use initWithFormat: and explicit release messages, in order to avoid running out of memory by piling up too many string objects in the autorelease pool.

And always, always, always remember to escape the values correctly before passing them to the formatting method.

Escaping (along with unescaping) is a really good thing to write unit tests for. Write a function to CSV-format a single row, and have test cases that compare its result to correct output. If you have a CSV parser on hand, or you're going to need one, or you just want to be really sure your escaping is correct, write unit tests for the parsing and unescaping as well as the escaping and formatting.

If you can start with a single record containing any combination of CSV-special and/or SQL-special characters, format it, parse the formatted string, and end up with a record equal to the one you started with, you know your code is good.

(All of the above applies equally to CSV and to HTML. If possible, you might consider using XHTML, so that you can use XML validation tools and parsers, including NSXMLParser.)

Peter Hosey