java CSV file to array

I am novice to java however, I cannot seem to figure this one out. I have a CSV file in the following format:

String1,String2
String1,String2
String1,String2
String1,String2

Each line are pairs. The 2nd line is a new record, same with the 3rd. In the real word the CSV file will change in size, sometimes it will be 3 records, or 4, or even 10.

My issues is how do I read the values into an array and dynamically adjust the size? I would imagine, first we would have to parse though the csv file, get the number of records/elements, then create the array based on that size, then go though the CSV again and store it in the array.

I'm just not sure how to accomplish this.

Any help would be appreciated.


Solution 1:

You can use ArrayList instead of Array. An ArrayList is a dynamic array. ex.

Scanner scan = new Scanner(new File("yourfile"));
ArrayList<String[]> records = new ArrayList<String[]>();
String[] record = new String[2];
while(scan.hasNext())
{
    record = scan.nextLine().split(",");
    records.add(record);
}
//now records has your records.
//here is a way to loop through the records (process)
    for(String[] temp : records)
    {
        for(String temp1 : temp)
        {
            System.out.print(temp1 + " ");
        }
        System.out.print("\n");
    }

Just replace "yourfile" with the absolute path to your file.

You could do something like this.

More traditional for loop for processing the data if you don't like the first example:

    for(int i = 0; i < records.size(); i++)
    {
        for(int j = 0; j < records.get(i).length; j++)
        {
            System.out.print(records.get(i)[j] + " ");
        }
        System.out.print("\n");
    }

Both for loops are doing the same thing though.

Solution 2:

You can simply read the CSV into a 2-dimensional array just in 2 lines with the open source library uniVocity-parsers.

Refer to the following code as an example:

public static void main(String[] args) throws FileNotFoundException {

    /**
     * ---------------------------------------
     * Read CSV rows into 2-dimensional array
     * ---------------------------------------
     */

    // 1st, creates a CSV parser with the configs
    CsvParser parser = new CsvParser(new CsvParserSettings());

    // 2nd, parses all rows from the CSV file into a 2-dimensional array
    List<String[]> resolvedData = parser.parseAll(new FileReader("/examples/example.csv"));

    // 3rd, process the 2-dimensional array with business logic
    // ......
}

Solution 3:

tl;dr

  • Use the Java Collections rather than arrays, specifically a List or Set, to auto-expand as you add items.
  • Define a class to hold your data read from CSV, instantiating an object for each row read.
  • Use the Apache Commons CSV library to help with the chore of reading/writing CSV files.

Class to hold data

Define a class to hold the data of each row being read from your CSV. Let's use Person class with a given name and surname, to be more concrete than the example in your Question.

In Java 16 and later, more briefly define the class as a record.

record Person ( String givenName , String surname ) {}

In older Java, define a conventional class.

package work.basil.example;

public class Person {
    public String givenName, surname;

    public Person ( String givenName , String surname ) {
        this.givenName = givenName;
        this.surname = surname;
    }

    @Override
    public String toString ( ) {
        return "Person{ " +
                "givenName='" + givenName + '\'' +
                " | surname='" + surname + '\'' +
                " }";
    }
}

Collections, not arrays

Using the Java Collections is generally better than using mere arrays. The collections are more flexible and more powerful. See Oracle Tutorial.

Here we will use the List interface to collect each Person object instantiated from data read in from the CSV file. We use the concrete ArrayList implementation of List which uses arrays in the background. The important part here, related to your Question, is that you can add objects to a List without worrying about resizing. The List implementation is responsible for any needed resizing.

If you happen to know the approximate size of your list to be populated, you can supply an optional initial capacity as a hint when creating the List.

Apache Commons CSV

The Apache Commons CSV library does a nice job of reading and writing several variants of CSV and Tab-delimited formats.

Example app

Here is an example app, in a single PersoIo.java file. The Io is short for input-output.

Example data.

GivenName,Surname
Alice,Albert
Bob,Babin
Charlie,Comtois
Darlene,Deschamps

Source code.

package work.basil.example;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class PersonIo {
    public static void main ( String[] args ) {
        PersonIo app = new PersonIo();
        app.doIt();
    }

    private void doIt ( ) {
        Path path = Paths.get( "/Users/basilbourque/people.csv" );
        List < Person > people = this.read( path );
        System.out.println( "People: \n" + people );
    }

    private List < Person > read ( final Path path ) {
        Objects.requireNonNull( path );
        if ( Files.notExists( path ) ) {
            System.out.println( "ERROR - no file found for path: " + path + ". Message # de1f0be7-901f-4b57-85ae-3eecac66c8f6." );
        }

        List < Person > people = List.of(); // Default to empty list.
        try {
            // Hold data read from file.
            int initialCapacity = ( int ) Files.lines( path ).count();
            people = new ArrayList <>( initialCapacity );

            // Read CSV file.
            BufferedReader reader = Files.newBufferedReader( path );
            Iterable < CSVRecord > records = CSVFormat.RFC4180.withFirstRecordAsHeader().parse( reader );
            for ( CSVRecord record : records ) {
                // GivenName,Surname
                // Alice,Albert
                // Bob,Babin
                // Charlie,Comtois
                // Darlene,Deschamps
                String givenName = record.get( "GivenName" );
                String surname = record.get( "Surname" );

                // Use read data to instantiate.
                Person p = new Person( givenName , surname );

                // Collect
                people.add( p ); // For real work, you would define a class to hold these values.
            }

        } catch ( IOException e ) {
            e.printStackTrace();
        }
        return people;
    }


}

When run.

People:

[Person{ givenName='Alice' | surname='Albert' }, Person{ givenName='Bob' | surname='Babin' }, Person{ givenName='Charlie' | surname='Comtois' }, Person{ givenName='Darlene' | surname='Deschamps' }]