There is a serious problem with using

String[] strArr=line.split(",");

in order to parse CSV files, and that is because there can be commas within the data values, and in that case you must quote them, and ignore commas between quotes.

There is a very very simple way to parse this:

/**
* returns a row of values as a list
* returns null if you are past the end of the input stream
*/
public static List<String> parseLine(Reader r) throws Exception {
    int ch = r.read();
    while (ch == '\r') {
        //ignore linefeed chars wherever, particularly just before end of file
        ch = r.read();
    }
    if (ch<0) {
        return null;
    }
    Vector<String> store = new Vector<String>();
    StringBuffer curVal = new StringBuffer();
    boolean inquotes = false;
    boolean started = false;
    while (ch>=0) {
        if (inquotes) {
            started=true;
            if (ch == '\"') {
                inquotes = false;
            }
            else {
                curVal.append((char)ch);
            }
        }
        else {
            if (ch == '\"') {
                inquotes = true;
                if (started) {
                    // if this is the second quote in a value, add a quote
                    // this is for the double quote in the middle of a value
                    curVal.append('\"');
                }
            }
            else if (ch == ',') {
                store.add(curVal.toString());
                curVal = new StringBuffer();
                started = false;
            }
            else if (ch == '\r') {
                //ignore LF characters
            }
            else if (ch == '\n') {
                //end of a line, break out
                break;
            }
            else {
                curVal.append((char)ch);
            }
        }
        ch = r.read();
    }
    store.add(curVal.toString());
    return store;
}

There are many advantages to this approach. Note that each character is touched EXACTLY once. There is no reading ahead, pushing back in the buffer, etc. No searching ahead to the end of the line, and then copying the line before parsing. This parser works purely from the stream, and creates each string value once. It works on header lines, and data lines, you just deal with the returned list appropriate to that. You give it a reader, so the underlying stream has been converted to characters using any encoding you choose. The stream can come from any source: a file, a HTTP post, an HTTP get, and you parse the stream directly. This is a static method, so there is no object to create and configure, and when this returns, there is no memory being held.

You can find a full discussion of this code, and why this approach is preferred in my blog post on the subject: The Only Class You Need for CSV Files.

Answer from AgilePro on Stack Overflow
🌐
Mkyong
mkyong.com › home › java › how to read and parse csv file in java
How to read and parse CSV file in Java - Mkyong.com
December 26, 2020 - CSVParser csvParser = new CSVParserBuilder().withSeparator(';').build(); // custom separator try(CSVReader reader = new CSVReaderBuilder( new FileReader(fileName)) .withCSVParser(csvParser) // custom CSV parser .withSkipLines(1) // skip the ...
🌐
Apache Commons
commons.apache.org › proper › commons-csv › apidocs › org › apache › commons › csv › CSVParser.html
CSVParser (Apache Commons CSV 1.14.2-SNAPSHOT API)
For those who like fluent APIs, ...CEL.parse(in)) { ... } To parse a CSV input from a file, you write: File csvData = new File("/path/to/csv"); CSVParser parser = CSVParser.parse(csvData, CSVFormat.RFC4180); for (CSVRecord csvRecord : parser) { ......
🌐
Apache Commons
commons.apache.org › proper › commons-csv › jacoco › org.apache.commons.csv › CSVParser.java.html
CSVParser.java - Apache Commons
* * For those who like fluent APIs, ... * <p> * To parse a CSV input from a file, you write: * </p> * * <pre>{@code * File csvData = new File("/path/to/csv"); * CSVParser parser = CSVParser.parse(csvData, CSVFormat.RFC4180); * for (CSVRecord csvRecord : parser) { * .....
🌐
Medium
medium.com › @zakariafarih142 › mastering-csv-parsing-in-java-comprehensive-methods-and-best-practices-a3b8d0514edf
Mastering CSV Parsing in Java: Comprehensive Methods and Best Practices | by Zakariafarih | Medium
November 25, 2024 - package org.example; import java.io.File; import java.io.IOException; import java.util.Scanner; public class CSVParserScanner { /* Pros: Simple to implement. Cons: Similar limitations as the split approach.
🌐
Blogger
javarevisited.blogspot.com › 2015 › 06 › 2-ways-to-parse-csv-files-in-java-example.html
2 Ways to Parse CSV Files in Java - BufferedReader vs Apache Commons CSV Example
August 7, 2021 - Here is full code example of how to read CSV file in Java. This program contains two examples, first one read CSV file without using third party library and the second one parse file using Apache commons CSV, a new library for parsing CSV files.
🌐
CalliCoder
callicoder.com › java-read-write-csv-file-apache-commons-csv
Read / Write CSV files in Java using Apache Commons CSV | CalliCoder
February 18, 2022 - The example below shows how you can read and parse the sample CSV file users.csv described above using Apache Commons CSV - import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; import java.io.IOException; import java.io.Reader; import java.nio.file.Files; import java.nio.file.Paths; public class BasicCSVReader { private static final String SAMPLE_CSV_FILE_PATH = "./users.csv"; public static void main(String[] args) throws IOException { try ( Reader reader = Files.newBufferedReader(Paths.get(SAMPLE_CSV_FILE_PATH)); CSVParser
🌐
Tabnine
tabnine.com › home › code library
Code Library - Tabnine
July 25, 2024 - Get the answers and suggestions you need from our AI code assistant. Get started in minutes with a free 90 day trial of Tabnine Pro.
🌐
CodersLegacy
coderslegacy.com › home › learn java › java csv parser – apache commons
Java CSV Parser - Apache Commons - CodersLegacy
October 6, 2022 - import org.apache.commons.csv.*; import java.io.*; public class example { public static void main(String[] args) throws IOException { CSVParser parser; FileReader Data = new FileReader("Data.txt"); parser = CSVParser.parse(Data, CSVFormat.DEFAULT); ...
Find elsewhere
Top answer
1 of 10
19

There is a serious problem with using

String[] strArr=line.split(",");

in order to parse CSV files, and that is because there can be commas within the data values, and in that case you must quote them, and ignore commas between quotes.

There is a very very simple way to parse this:

/**
* returns a row of values as a list
* returns null if you are past the end of the input stream
*/
public static List<String> parseLine(Reader r) throws Exception {
    int ch = r.read();
    while (ch == '\r') {
        //ignore linefeed chars wherever, particularly just before end of file
        ch = r.read();
    }
    if (ch<0) {
        return null;
    }
    Vector<String> store = new Vector<String>();
    StringBuffer curVal = new StringBuffer();
    boolean inquotes = false;
    boolean started = false;
    while (ch>=0) {
        if (inquotes) {
            started=true;
            if (ch == '\"') {
                inquotes = false;
            }
            else {
                curVal.append((char)ch);
            }
        }
        else {
            if (ch == '\"') {
                inquotes = true;
                if (started) {
                    // if this is the second quote in a value, add a quote
                    // this is for the double quote in the middle of a value
                    curVal.append('\"');
                }
            }
            else if (ch == ',') {
                store.add(curVal.toString());
                curVal = new StringBuffer();
                started = false;
            }
            else if (ch == '\r') {
                //ignore LF characters
            }
            else if (ch == '\n') {
                //end of a line, break out
                break;
            }
            else {
                curVal.append((char)ch);
            }
        }
        ch = r.read();
    }
    store.add(curVal.toString());
    return store;
}

There are many advantages to this approach. Note that each character is touched EXACTLY once. There is no reading ahead, pushing back in the buffer, etc. No searching ahead to the end of the line, and then copying the line before parsing. This parser works purely from the stream, and creates each string value once. It works on header lines, and data lines, you just deal with the returned list appropriate to that. You give it a reader, so the underlying stream has been converted to characters using any encoding you choose. The stream can come from any source: a file, a HTTP post, an HTTP get, and you parse the stream directly. This is a static method, so there is no object to create and configure, and when this returns, there is no memory being held.

You can find a full discussion of this code, and why this approach is preferred in my blog post on the subject: The Only Class You Need for CSV Files.

2 of 10
19

You also have the Apache Commons CSV library, maybe it does what you need. See the guide. Updated to Release 1.1 in 2014-11.

Also, for the foolproof edition, I think you'll need to code it yourself...through SimpleDateFormat you can choose your formats, and specify various types, if the Date isn't like any of your pre-thought types, it isn't a Date.

🌐
MojoAuth
mojoauth.com › parse-and-generate-formats › parse-and-generate-csv-with-java
Parse and Generate CSV with Java | Parse and Generate Formats
Here's a practical example using Apache Commons CSV to read records from a file named data.csv: import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; import java.io.FileReader; import java.io.IOException; import java.io.Reader; public class CsvReaderExample { public static void main(String[] args) throws IOException { try (Reader reader = new FileReader("data.csv"); CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT.withHeader())) { for (CSVRecord record : csvParser) { String name = record.get("Name"); String age = record.get("Age"); System.out.println("Name: " + name + ", Age: " + age); } } } }
🌐
Oracle
docs.oracle.com › middleware › 12213 › adf › api-reference-model › oracle › adf › model › adapter › dataformat › csv › CSVParser.html
CSVParser (Oracle Fusion Middleware Java API Reference for Oracle ADF Model)
Examples: 1. ,"abc 123", => abc 123 2. ,"abc" "123", => abc "123" 3. ,abc "123", => abc "123" 4. ,"abc "" 123", => abc " 123 ... UTF8 encoding, used for hadling data in different languages. ... public CSVParser(java.io.InputStream pInputStream, java.lang.String pEnc) throws java.lang.Exception
🌐
Simplesolution
simplesolution.dev › java-read-and-parse-csv-file-using-apache-commons-csv
Read and Parse CSV File in Java using Apache Commons CSV
import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; public class ParseCsvFromInputStreamExample { public static void main(String...
Top answer
1 of 4
9

Side-effects

Stream.forEach() operation should be utilized with care since it operates via side-effects and should not be used as a substitution of a proper reduction operation.

The way you've written this stream is discouraged by the Stream API documentation because it makes code more cluttered and difficult to follow and more importantly your solution is broken with parallel streams (there should be no assumptions on the nature of the stream in your code).

In this particular case, you should be using Stream.toArray() operation instead.

Multiline lambdas

Try to void them. They bear a lot of cognitive load. If a lambda expression requires several lines or when you have a complex single-line lambda (e.g. with a nested stream in it), consider introducing a method.

Exceptions

In short, the purpose of exceptions is to indicate cases when it's not possible to proceed with the normal execution flow.

If you stumbled on a corrupt piece of data which violates invariant that are important for your business logic, usually you don't want to proceed processing it. That's a valid case to throw. You asked can you "throw from a stream"? Sure, it's just a means iteration.

I've seen some bickering over whether it's appropriate to use exceptions for the purpose of validation. Sure it is, we do employ Exceptions for Validation for decades.

Unless you're using exceptions to avoid conditional logic, or to make weird hacks like throwing in order to break from a recursive method, and you have a genuine invalid piece of data on your hands you can and should throw.

Another, important note: exceptions should be informative. If standard exception types can describe the case at hand, fine, if not introduce your own exception type.

Also, use proper exception messages that will be helpful in investigating the issue.

Static routines

Don't treat everything as util classes, use the Power of object-orientation to make more clean, cohesive and testable.

Refactored version

public class ArrayParser {
    private final String separator;
    private final int columnCount;
    
    public ArrayParser(String separator, int columnCount) {
        this.separator = separator;
        this.columnCount = columnCount;
    }
    
    public String[][] parse(final String str) {
        return str.lines()
            .map(this::parseLine)
            .toArray(String[][]::new);
    }
    
    private String[] parseLine(String toParse) {
        String[] line = toParse.split(separator);
        validateLine(line);
        return line;
    }
    
    private void validateLine(String[] line) {
        if (line.length != columnCount) {
            throw new LineParsingException(line, columnCount);
        }
    }
}

Exception example:

private class LineParsingException extends RuntimeException {
    private static final String MESSAGE_TEMPLATE = """
            The actual number of columns in the line
            %s
            doesn't match the expected number of columns %d""";
    
    public LineParsingException(String[] line, int columnsExpected) {
        super(MESSAGE_TEMPLATE.formatted(Arrays.toString(line), columnsExpected));
    }
}
2 of 4
7

conservative design

Since this is billed as "a CSV parser", a caller may reasonably believe they could send it any *.csv file produced by Excel. Better to advertise it as MyRestrictedCsvParser. The /** javadoc */ comments should explain the restrictions.

  1. Each field may or may not be enclosed in double quotes

This library should probably throw a fatal error upon encountering an ASCII 34 " double quote anywhere in an input line. Then a caller would not accidentally consume a data file in the belief that it had been parsed one way when in fact the library parsed it another way. That is, part of scoping down requirements is reducing the space of inputs you're willing to claim you successfully processed.

informative diagnostic

Throwing an unchecked exception within the JVM is great. It makes your library easier for callers to consume.

                throw new RuntimeException();

This is not a very diagnostic error. It needs two improvements:

  1. Subclass RuntimeException to create a library-specific error, perhaps CsvParseException.
  2. Mention the values of split.length and cols in the message, to save a maintenance engineer a little effort in diagnosing and repairing buggy inputs.

Consider keeping track of which line number we're on, so that can be included in the diagnostic message.

A caller should not be forced to catch a generic RuntimeException to recover from an error it knows how to deal with. We define new app-specific exception types to permit fine-grained catching. Lumping "wrong column count", "found a quote", and "zero lines" together would be acceptable, at least until you see how callers actually behave. If it turns out that callers really do wish to distinguish between those errors, then a v2 library release could always offer finer granularity on the error types.

signature

Clearly the OP code works. It seems slightly less convenient for the caller than it might be. There is redundant information encoded in the str and cols parameters.

Consider setting cols based on number of fields found in the first line of input.

🌐
GeeksforGeeks
geeksforgeeks.org › java › reading-csv-file-java-using-opencsv
Reading a CSV file in Java using OpenCSV - GeeksforGeeks
July 11, 2025 - CSVParser parser = new CSVParserBuilder().withSeparator(';').build(); Then we will create CSVReader object withCSVParser() method along with constructor and provided the made parser object to parameter of withCSVParser method.At last call build ...
🌐
Stack Abuse
stackabuse.com › reading-and-writing-csvs-in-java-with-apache-commons-csv
Reading and Writing CSVs in Java with Apache Commons CSV
February 20, 2019 - For example, if you want to avoid referring to the units of measure in the header in our tree data file, you can redefine the header to use your own string values: CSVParser csvParser = CSVFormat.REF4180.withHeader("Index", "Girth", "Height", "Volume"); for (CSVRecord record : csvParser) { ...
🌐
IBM
ibm.com › docs › en › imdm › 11.6.0
CSVParser Java sample
March 14, 2024 - This sample parser parses a reader stream and tokenizes the stream based on a comma (`,`). The stream is broken down into lines, which are then tokenized. An empty string token is returned if the parser finds two consecutive commas (`,`) or if the line starts or ends with a comma.
Top answer
1 of 1
4

We need to tell the parser to process the header line for us. We specify that as part of the CSVFormat, so we'll create a custom format like this:

CSVFormat csvFormat = CSVFormat.RFC4180.withFirstRecordAsHeader();

Question code used DEFAULT, but this is based on RFC4180 instead. Comparing them side-by-side:

DEFAULT                               RFC4180                       Comment
===================================   ===========================   ========================
withDelimiter(',')                    withDelimiter(',')            Same
withQuote('"')                        withQuote('"')                Same
withRecordSeparator("\r\n")           withRecordSeparator("\r\n")   Same
withIgnoreEmptyLines(true)            withIgnoreEmptyLines(false)   Don't ignore blank lines
withAllowDuplicateHeaderNames(true)   -                             Don't allow duplicates
===================================   ===========================   ========================
                                      withFirstRecordAsHeader()     We need this

With that change, we can call get(String name) instead of get(int i):

User currentUser = new User(
        Integer.parseInt(csvRecord.get("id")),
        csvRecord.get("first"),
        csvRecord.get("last"),
        csvRecord.get("city")
);

Note that CSVParser implements Iterable<CSVRecord>, so we can use a for-each loop, which makes the code look like this:

String path = "./data/data.csv";

Map<Integer, User> map = new HashMap<>();
try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(Paths.get(path)),
                                         CSVFormat.RFC4180.withFirstRecordAsHeader())) {
    for (CSVRecord csvRecord : csvParser) {
        User currentUser = new User(
                Integer.parseInt(csvRecord.get("id")),
                csvRecord.get("first"),
                csvRecord.get("last"),
                csvRecord.get("city")
        );
        map.put(currentUser.getId(), currentUser);
    }
}

That code correctly parses the file, even if the column order changes, e.g. to:

last,first,id,city
doe,john,1,austin
mary,jane,2,seattle
🌐
How to do in Java
howtodoinjava.com › home › i/o › parse and read a csv file in java
Parse and Read a CSV File in Java
September 14, 2022 - In Java, there are different ways of reading and parsing CSV files. Let's discuss some of the best approaches such as OpenCSV, Super CSV etc.