How to read a cvs file into NSArray?

Discussion in 'Mac Programming' started by vukid, Oct 10, 2013.

  1. vukid, Oct 10, 2013
    Last edited: Oct 10, 2013

    vukid macrumors newbie

    Joined:
    Oct 10, 2013
    #1
    I need to read in a csv file into a string and break all the comma separated words

    i.e. : house, city, street, zip

    and then put them into arrays so I can output the the rows in the cvs individually, but i'm an new to coding so I do not know where to start, any help would be great.
     
  2. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #2
    What you say doesn't make any sense.

    Are you sure you want to read cvs files? Canvas 3 drawing file? I would find it highly impolite if you couldn't even be bothered to check the spelling of what you are asking for.
     
  3. Niklas Korz macrumors newbie

    Joined:
    Oct 7, 2013
    Location:
    Germany
    #3
    Well, basically it's pretty easy. For my example, I'll assume that you've already read the CSV file and put it's content into a string called "content".

    Code:
    NSMutableArray *entries = [[NSMutableArray alloc] init];
    for (NSString *line in [content componentsSeparatedByString:@"\n"]) {
        NSArray *rows = [line componentsSeparatedByString:@";"]; // ';' can be any separator, e.g. \t or just a comma
        [entries addObject:rows];
    }
    
    There's probably a more efficient way, but you get the idea.
     
  4. gnasher729, Oct 10, 2013
    Last edited: Oct 10, 2013

    gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #4
    A method that actually works would be preferable. For example, if you get a line like

    1997,Ford,E350,"Super, luxurious truck"

    (from the Wikipedia article)

    Just search for "CSV file format", figure out what encoding and what separator, and then read the file into an NSString and examine the characters byte by byte, check for quotes, double quotes, separators, carriage returns, line feeds. I mean what are the bets that the file comes from a Windows machine, code page 1252, CR/LF pairs?
     
  5. vukid thread starter macrumors newbie

    Joined:
    Oct 10, 2013
    #5
    I still am not sure how to read a .cvs file to a String yet, some help on that will help also. How would output that for NSLog?
     
  6. devilofspades, Oct 10, 2013
    Last edited by a moderator: Oct 10, 2013

    devilofspades macrumors member

    Joined:
    Jul 20, 2011
    #6
    Code:
    NSString *sourceFileString = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"filename" ofType:@"csv"] encoding:NSUTF8StringEncoding error:nil];
    
    
    NSMutableArray *csvArray = [[NSMutableArray alloc] init];
        
    csvArray = [[sourceFileString componentsSeparatedByString:@"\n"] mutableCopy];
    
    NSString *keysString = [csvArray objectAtIndex:0];
        
    NSArray *keysArray = [keysString componentsSeparatedByString:@","];
        
    [csvArray removeObjectAtIndex:0];
    from here it all depends on where you want to go. i use this same process to create dictionaries of and nest those in another dictionary. but each use case is going to be different. this is a good jumping off point for you to work out the rest.
     
  7. gnasher729, Oct 10, 2013
    Last edited by a moderator: Oct 10, 2013

    gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #7
    I think you are making a wrong assumption here. The OP wants to read .cvs files, which are a totally different thing. He said it twice. After being asked to check.
     
  8. vukid thread starter macrumors newbie

    Joined:
    Oct 10, 2013
    #8
    yes, i need to read the .cvs file to a string then create arrays to where i can output a whole row with the home, city,........ that matches what a user input wants to see.
     
  9. devilofspades macrumors member

    Joined:
    Jul 20, 2011
    #9
    this is exactly what this code does. if you took the time to look at it you would see that. as i said, each case is going to be different and vary depending how the csv is formatted. the "op" also needs to understand what is going on and can't expect to cut and paste code and expect to have a successful app. once they figure this out, they will realize that an array is not what they want. its a good start, but in the end a csv is 2 dimensional array, and hence is better suited for a dictionary or nested dictionaries.
     
  10. vukid thread starter macrumors newbie

    Joined:
    Oct 10, 2013
    #10
    I'm pretty new to objective c, sorry, I would I go about outputting a name match from the array above to a user input?

    ----------

    Also, what folder on the computer would i put the .cvs file so that xcode can read the file?
     
  11. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #11
    Instead of having other people write your program for you in a series of questions, please tell us what you do know about Objective-C. Also describe what you know about programming in general, such as any other programming languages you already know.

    If you studied an online tutorial, what is its URL, and exactly where are you in studying it?

    If you're working from a book, exactly which book? Title, author, edition.

    If you haven't done anything, and are expecting to have a working program after just some copy-and-paste from randomly collected pieces of code, then your expectation is mistaken.
     
  12. devilofspades macrumors member

    Joined:
    Jul 20, 2011
    #12
    ok, so the code i posted breaks down line this. first it takes the entire csv file and makes a string out of it. it then takes that string and then creates and array of each line or "row" separated by a line break. from there it creates another array (keysArray) using the first "line" of the csv file stored in the previous array, these are your "column headers". what's left is an array of each "row" of the csv file. this is where it gets tricky and depends on how you plan to use it. each object in the array is a row from the csv, but that info is useless without some context to the column title / name / header, etc. this is why i said it would depend on the use case. my suggestions / recommendation (also the direction my code is headed) is to create separate dictionaries for each row of the csv. the keys of that dictionary would be the column titles. from there you would essentially have a 2 row csv. then if you did that for every row you could then encapsulate each of those dictionaries in array, or better yet another dictionary with a unique key name for each row you stored as a dictionary, such as a persons name or address etc. this way you could extract the whole "row" of information by a key value and that "row" would already be organized by keys of the "column names".

    this all starting to make sense?
     
  13. vukid thread starter macrumors newbie

    Joined:
    Oct 10, 2013
    #13
    I understand the logic, just not the coding itself since i am coming from c++. Can you show me from the code above what i have to do next to get the name of the user input from the array?
     
  14. devilofspades macrumors member

    Joined:
    Jul 20, 2011
    #14
    if you could give me a simplified version of the csv you will be using i can break it down. i just need you to put something with just 3 or 4 lines. also, let me know how you want to reference or pull that data once its stored. the thing to keep in mind is that an array stores data by index numbers. nothing can be retrieved via a string name, hence my suggestion to use dictionaries.

    for everyone else reading this, disregard tableviews and "row at index" for now as i don't want to over complicate this.
     
  15. vukid, Oct 10, 2013
    Last edited: Oct 10, 2013

    vukid thread starter macrumors newbie

    Joined:
    Oct 10, 2013
    #15
    I need to prompt user to enter a city name and then output the city prompt by user and display it on console,
    my cvs file looks like this:

    City,Country,Latitude,Longitude
    Aberdeen, Scotland,57.15,-2.15
    Adelaide, Australia,-34.91666667,138.6
    Algiers, Algeria,36.83333333,3
    Amsterdam, Netherlands,52.36666667,4.883333333
    Ankara, Turkey,39.91666667,32.91666667
    ....


    Code:
    #import <Foundation/Foundation.h>
    #import <string.h>
    int main(int argc, const char * argv[])
    
    {
        @autoreleasepool {
            
            //input user for a name on city list       
             char input[100];
    
            do 
            {
                
                NSLog(@"The worldcities.cvs contained infor on 120 cities.");         
                NSLog(@"Enter a city by name: ");   
                fgets( input, 100, stdin );
                NSString *sourceFileString = [NSString stringWithContentsOfFile:      [[NSBundle mainBundle] pathForResource:@"worldcities" ofType:@"csv"] encoding:NSUTF8StringEncoding error:nil];
                
                NSMutableArray *csvArray = [[NSMutableArray alloc] init];
                
                csvArray = [[sourceFileString componentsSeparatedByString:@"\n"] mutableCopy];
                
                NSString *keysString = [csvArray objectAtIndex:0];
                
                NSArray *keysArray = [keysString componentsSeparatedByString:@","];
                
                [csvArray removeObjectAtIndex:0];
    }while(true)
    }
    return 0;
    
    your code above also has a unused variable 'keysArray' pop up when ran.
     
  16. devilofspades, Oct 10, 2013
    Last edited: Oct 10, 2013

    devilofspades macrumors member

    Joined:
    Jul 20, 2011
    #16
    if your starting csv looks like this...

    City,Country,Latitude,Longitude
    Aberdeen, Scotland,57.15,-2.15
    Adelaide, Australia,-34.91666667,138.6
    Algiers, Algeria,36.83333333,3
    Amsterdam, Netherlands,52.36666667,4.883333333
    Ankara, Turkey,39.91666667,32.91666667


    then here is the process..

    Code:
    // sets a string to be the value of the file
        NSString *sourceFileString = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"your filename" ofType:@"csv"] encoding:NSUTF8StringEncoding error:nil];
    
    your string will be a multiline representation of your csv file. 
    
    // create an editable array
        NSMutableArray *csvArray = [[NSMutableArray alloc] init];
        
        
    // set the contents of the array to the contents of the string separated by line break
        csvArray = [[sourceFile componentsSeparatedByString:@"\n"] mutableCopy];
        
        
    // create a string with the value of the title / name of the "columns" by pulling the first entry of the array
        NSString *keysString = [csvArray objectAtIndex:0];
           
    your result will be @"City,Country,Latitude,Longitude"
    
        
    // create an array to store the column names seperated by commas
        NSArray *keysArray = [keysString componentsSeparatedByString:@","];
        
    *your result will be (
    			City, <--index:0 
    			Country, <--index:1
    			Latitude, <--index:2
    			Longitude <--index:3
    			)
    
        
        
    // remove the first object in the array that is storing the column names, leaving only data left
        [csvArray removeObjectAtIndex:0];
       
    **your csvArray will now be (
    				Aberdeen, Scotland,57.15,-2.15  <--index:0
    				Adelaide, Australia,-34.91666667,138.6 <--etc…
    				Algiers, Algeria,36.83333333,3
    				Amsterdam, Netherlands,52.36666667,4.883333333
    				Ankara, Turkey,39.91666667,32.91666667
    				)
    
    
        
    // create an editable dictionary to store final data
        NSMutableDictionary *outputDict = [[NSMutableDictionary alloc]init];
        
        
    // create a while loop to keep track of the amount of items in the csvArray
    // a "nil" value is always the last item in an array or dictionary hence the value of 1 and not 0
    // 1 would essentially be an "empty" array or dictionary
        while (csvArray.count > 1)
        {
            
            // create a string to store each line of info from the first item in the array
            NSString *tempString = [csvArray objectAtIndex:0];
    
    your first result would be @"Aberdeen, Scotland,57.15,-2.15"
    this will change for every sequence of the loop
            
            
            
    // create an array to store those items seperated by a comma
            NSArray *tempArray = [tempString componentsSeparatedByString:@","];
    
    your first result would be (
    				Aberdeen, 
    				Scotland,
    				57.15,
    				-2.15  
    				)     
            
            
    // creates a temp dictionary to store the contents of the tempArray with key values of the column names stored in keysArray
            NSDictionary *tempDictionary = [[NSDictionary alloc]initWithObjects:tempArray forKeys:keysArray];
            
    
    your result would be {
    			Aberdeen = City;
    			Scotland = Country;
    			57.15 = Latitude;
    			-2.15 = Longitude; 
    			}    
            
            
    // adds the tempDictionary to the master dictionary with a key value of the city name that was parsed
    // from the tempArray (see *)
            [outputDict setObject:tempDictionary forKey:[tempArray objectAtIndex:0]];
            
            
            
    // removes the first item in the array (the first "row" of the csv) which bumps the next line in the array up to index 0 (see **)
            [csvArray removeObjectAtIndex:0];
            
            
            // will repeat and remove one row at a time until the "master dictionary" is filled with a each "city" dictionary
        }
        
        
    to access the data by city you would just call
    Code:
      [outputDict objectForKey:@"the city name you want"]; 
    if you want say the longitude for Aberdeen, you would use
    Code:
     [outputDict valueForKeyPath:@"Aberdeen.Longitude"]; 

    that's about as detailed as i can and willing to get. just don't cut and paste this, learn how and what is being done. the reason you got an "unused variable" is because that wasn't the complete set of code. also, i typed all this out in texteditor so i would manually type it all out in xcode because im sure there are a ton of typos.
     
  17. vukid thread starter macrumors newbie

    Joined:
    Oct 10, 2013
    #17
    thank you, I will try and see if this will work with what I want to use the data for.
     
  18. devilofspades macrumors member

    Joined:
    Jul 20, 2011
    #18
    you can see from the code that the csv is parsed into multiple arrays as per your original request but you can some what infer by the output and comments that this data is somewhat useless simply because of the lack of "organization". you could store all those arrays into other arrays but retrieving granular data becomes difficult unless you implement a tableview that can display all the info.
     
  19. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #19
    You wrote code to read .csv files. That's not what the OP asked for. He asked for code to read .cvs files.

    http://www.fileinfo.com/extension/cvs

    What he _wants_, I don't know. But .cvs is what he asked for, not .csv.
     
  20. devilofspades, Oct 10, 2013
    Last edited: Oct 10, 2013

    devilofspades macrumors member

    Joined:
    Jul 20, 2011
    #20
    im sure the op can confirm that "cvs" is a typo. given what it is they want to do and using some, ummm what's the term...common sense, and seeing that other posters pointed out the same info. we can pretty much figure when the op put

    it's a safe bet they meant csv or "comma separated values"

    http://www.fileinfo.com/extension/csv
     
  21. gnasher729, Oct 10, 2013
    Last edited: Oct 10, 2013

    gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #21
    See my first post. He surely never confirmed that. Maybe he wants to read comma veparated salues. :D

    Anyway: In a file with comma separated values, if an item starts with a quote character ", then it extends up to the next single quote character, even if there are commas and carriage return or linefeed characters in between. The first and last quote should be removed, and double quotes inside replaced with single quotes.
     
  22. devilofspades macrumors member

    Joined:
    Jul 20, 2011
    #22
    ideally one needs to check the formatting of their source file, but the process i gave doesn't care wether there are quotes or not. if there are, then you will just end up with double or single quotes in your string names. if you have double quotes in your csv then it will still separate it at the comma. most csv files won't have double quotes anyways since spaces are acceptable in a csv.
     
  23. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #23
    Quoting is also used to embed a comma (or other special character) in a field value. Without quotes, an embedded comma would be a delimiter. With quotes, the comma is NOT a delimiter. Your code doesn't handle this case.
    http://en.wikipedia.org/wiki/Comma-separated_values#Toward_standardization

    It's unclear whether the code even needs to handle quoted commas. If the data were really house, city, state, zip as posted in the 1st post, then it's conceivable a comma could appear in the "house" field. But since the data isn't that, it's unclear what the real constraints for the data are.
     
  24. vukid thread starter macrumors newbie

    Joined:
    Oct 10, 2013
    #24
    Check the posts below I commented, I need to read a comma separated file into arrays n output the correct array to console based on user string input match?
     
  25. robvas macrumors 68020

    Joined:
    Mar 29, 2009
    Location:
    USA
    #25
    You sure you don't want to load them into CoreData and that way you can query etc?
     

Share This Page