OpenCSV: Read and Write CSV Files in Java

OpenCSV 5.12 examples to read and write CSV files in Java as arrays, maps and beans, with quoting, appending, bad rows and large files explained.

OpenCSV reading books.csv into String arrays, maps and Book beans, and writing beans back to CSV

OpenCSV is a Java library that reads and writes CSV files, handling quoted values, commas inside fields and escaped quotes for us. It reads rows as String arrays or maps them straight to Java beans, and it writes arrays, beans or a JDBC ResultSet back to CSV.

We use OpenCSV to import and export data that spreadsheets and other systems exchange as CSV, such as product catalogs, bank transactions or report downloads. Splitting lines on commas by hand breaks on the first value that contains a comma, which is the problem OpenCSV solves.

The following example reads a small book list with OpenCSV 5.12.0, maps it to Book beans and writes one more row.

List<Book> books = new CsvToBeanBuilder<Book>(Files.newBufferedReader(Path.of("books.csv")))
        .withType(Book.class)
        .build()
        .parse();
int count = books.size();                    // 3
String second = books.get(1).getTitle();     // "Java, A Beginner's Guide"

StringWriter out = new StringWriter();
try (CSVWriter writer = new CSVWriter(out)) {
    writer.writeNext(new String[]{"Dune", "Frank Herbert", "1965"});
}
List<String> line = out.toString().lines().toList();   // ["Dune","Frank Herbert","1965"]

Notice that the title with a comma inside comes through as one value, and that CSVWriter puts quotes around every field by default. We set up the dependency, read files line by line and as maps, map rows to beans, write and append files, and handle bad data, quotes and large files.

1. Setting Up

OpenCSV 5.12.0 is the current release. It supports Java 8 and later and runs on Java 25, and it pulls in Apache Commons Lang, Commons Text, Commons BeanUtils and Commons Collections.

<dependency>
    <groupId>com.opencsv</groupId>
    <artifactId>opencsv</artifactId>
    <version>5.12.0</version>
</dependency>

All examples read the following books.csv file. The second title contains a comma, so the CSV format requires quotes around it.

title,author,year
"Effective Java",Joshua Bloch,2018
"Java, A Beginner's Guide",Herbert Schildt,2024
Clean Code,Robert Martin,2008

2. Main Classes of the OpenCSV API

OpenCSV has a low-level API that works with String[] rows and a bean API on top of it. Most apps need the bean API for reading and writing typed objects, and the low-level classes for quick scripts or files without a fixed structure.

ClassUse it for
CSVReaderReading rows as String[] with readNext(), readAll() or a for-each loop
CSVReaderHeaderAwareReading rows as Map<String, String> keyed by the header names
CSVReaderBuilder, CSVParserBuilderConfiguring skipped lines, separators, quote characters and the parser
CSVWriterWriting String[] rows or a JDBC ResultSet
CsvToBeanBuilderMapping rows to beans with @CsvBindByName or @CsvBindByPosition
StatefulBeanToCsvBuilderWriting a list of beans, including the header line

The old BeanToCsv class and the setter-based CsvToBean setup from OpenCSV 3 and 4 were removed in version 5. Code that calls csv.setCsvReader() or new BeanToCsv() does not compile with OpenCSV 5, and the builders replace them.

3. Parsing and Reading a CSV File

A CSV parser walks through the text character by character and tracks whether it is inside quotes, so a comma or a line break inside quotes stays part of the value. OpenCSV returns each record as a String array, and the caller converts the values to numbers or dates.

OpenCSV reading books.csv into String arrays, maps and Book beans, and writing beans back to CSV
OpenCSV offers three views of the same rows, String arrays, header maps and beans, and writes beans or arrays back with correct quoting

3.1. Reading a CSV File Line by Line

Reading line by line keeps only one record in memory, so it works for files of any size. The readNext() method returns null at the end of the file, and it declares CsvValidationException for lines that break the configured rules.

List<String[]> rows = new ArrayList<>();
try (CSVReader reader = new CSVReaderBuilder(Files.newBufferedReader(Path.of("books.csv")))
        .withSkipLines(1)                    // skip the header
        .build()) {
    String[] next;
    while ((next = reader.readNext()) != null) {
        rows.add(next);
    }
}
String[] first = rows.get(0);                // [Effective Java, Joshua Bloch, 2018]
int columns = rows.get(1).length;            // 3, the quoted comma stays in the title

A CSVReader is also Iterable<String[]>, so a for-each loop over the reader does the same as the while loop. We open the file with Files.newBufferedReader(), which reads UTF-8 by default, and close it with try-with-resources.

For a file that uses another separator, such as the semicolon that European Excel versions write, we pass a parser to the builder.

String[] fields;
try (CSVReader reader = new CSVReaderBuilder(new StringReader("Dune;Frank Herbert;1965"))
        .withCSVParser(new CSVParserBuilder().withSeparator(';').build())
        .build()) {
    fields = reader.readNext();
}
String[] semicolon = fields;                 // [Dune, Frank Herbert, 1965]

Avoid withIgnoreQuotations(true) unless the file has no quotes at all. With that option, the parser treats quotes as plain characters, so “Java, A Beginner’s Guide” splits into two values and the row gets four columns instead of three.

3.2. Reading the Complete CSV File at Once

The readAll() method returns every record as a List<String[]>. It is handy for small files such as a settings table, but it keeps the whole file in memory.

List<String[]> all;
try (CSVReader reader = new CSVReader(Files.newBufferedReader(Path.of("books.csv")))) {
    all = reader.readAll();
}
int lines = all.size();                      // 4, including the header

3.3. Reading Rows as Maps by Header Name

When the column order can change between file versions, positions are fragile. The CSVReaderHeaderAware class reads the first line as the header and returns each following row as a map from column name to value.

Map<String, String> row;
try (CSVReaderHeaderAware reader = new CSVReaderHeaderAware(Files.newBufferedReader(Path.of("books.csv")))) {
    row = reader.readMap();
}
String author = row.get("author");           // "Joshua Bloch"
String year = row.get("year");               // "2018"

4. Reading CSV into Java Beans

The bean API maps each column to a field and converts the text to the field type, such as int, BigDecimal or LocalDate. We mark the fields with @CsvBindByName to match header names, or with @CsvBindByPosition for files without a header.

public static class Book {
    @CsvBindByName(column = "title")
    private String title;

    @CsvBindByName(column = "author")
    private String author;

    @CsvBindByName(column = "year")
    private int year;

    public Book() {
    }

    public Book(String title, String author, int year) {
        this.title = title;
        this.author = author;
        this.year = year;
    }

    public String getTitle() { return title; }
    public String getAuthor() { return author; }
    public int getYear() { return year; }
}
List<Book> books = new CsvToBeanBuilder<Book>(Files.newBufferedReader(Path.of("books.csv")))
        .withType(Book.class)
        .build()
        .parse();
int year = books.get(0).getYear();           // 2018
String author = books.get(2).getAuthor();    // "Robert Martin"

Header matching ignores case, so Title and title both work. The bean needs a public no-argument constructor, which is why Java records do not work with CsvToBeanBuilder and fail with CsvBeanIntrospectionException. If the domain model uses records, we read into a small bean or into maps and convert.

The parse() method reads all rows into a list and processes them on several threads. For a large file, we iterate over the CsvToBean object instead, which creates one bean at a time on the calling thread.

List<String> titles = new ArrayList<>();
CsvToBean<Book> csvToBean = new CsvToBeanBuilder<Book>(Files.newBufferedReader(Path.of("books.csv")))
        .withType(Book.class)
        .build();
for (Book book : csvToBean) {
    titles.add(book.getTitle());
}
String last = titles.get(2);                 // "Clean Code"

5. Writing into a CSV File

The CSVWriter class quotes values and escapes quotes inside values by doubling them. By default, it puts quotes around every field and ends each line with \n.

StringWriter out = new StringWriter();
try (CSVWriter writer = new CSVWriter(out)) {
    writer.writeNext(new String[]{"Dune", "Frank Herbert", "1965"});
    writer.writeNext(new String[]{"Java, Guide", "Herbert Schildt", "2024"}, false);
    writer.writeNext(new String[]{"Say \"Hi\"", "Anna", null});
}
String csv = out.toString();
List<String> written = csv.lines().toList();  // ["Dune","Frank Herbert","1965", "Java, Guide",Herbert Schildt,2024, "Say ""Hi""","Anna",]

The second argument of writeNext(line, applyQuotesToAll) controls the quotes. With true (the default), every value is quoted. With false, only values that need quotes, such as Java, Guide with its comma, are quoted. A null value becomes an empty field.

To write to a file, we wrap a Writer from Files.newBufferedWriter() in the CSVWriter. Excel on Windows expects a byte order mark to detect UTF-8, so for files that users open in Excel, we write \uFEFF to the writer before the first row.

5.1. Writing Beans with StatefulBeanToCsv

The StatefulBeanToCsv class writes beans, header included. By default, it writes the header in upper case and sorts the columns alphabetically, which surprises most people. A HeaderColumnNameMappingStrategy with a column order fixes that.

HeaderColumnNameMappingStrategy<Book> strategy = new HeaderColumnNameMappingStrategy<>();
strategy.setType(Book.class);
List<String> order = List.of("TITLE", "AUTHOR", "YEAR");
strategy.setColumnOrderOnWrite(Comparator.comparingInt(order::indexOf));

StringWriter out = new StringWriter();
new StatefulBeanToCsvBuilder<Book>(out)
        .withMappingStrategy(strategy)
        .withApplyQuotesToAll(false)
        .build()
        .write(List.of(new Book("Dune", "Frank Herbert", 1965)));
List<String> beanLines = out.toString().lines().toList();   // [TITLE,AUTHOR,YEAR, Dune,Frank Herbert,1965]

The write() method declares CsvDataTypeMismatchException and CsvRequiredFieldEmptyException, so the calling method handles or declares them.

6. Appending to an Existing CSV File

OpenCSV has no append mode of its own, because appending is a property of the file stream. We open the file with StandardOpenOption.APPEND and write rows without a header.

Path copy = Files.copy(Path.of("books.csv"), Path.of("books-copy.csv"), StandardCopyOption.REPLACE_EXISTING);
try (CSVWriter writer = new CSVWriter(Files.newBufferedWriter(copy, StandardOpenOption.APPEND))) {
    writer.writeNext(new String[]{"Refactoring", "Martin Fowler", "2018"}, false);
}
List<String> appended = Files.readAllLines(copy);
String lastLine = appended.get(appended.size() - 1);   // "Refactoring,Martin Fowler,2018"

Appending works only if the existing file ends with a line break. Excel and many tools save the last row without one, and the new row is glued to the last line. The append to file article covers the file options in more detail.

7. Handling Bad Rows

A bank export with 50,000 transactions often has a few broken rows, for example an amount of n/a. By default, parse() stops at the first bad row with a RuntimeException that wraps a CsvDataTypeMismatchException, so one bad line rejects the whole file.

String data = "title,author,year\nX,Y,abc\nA,B,2001\n";
List<Book> strict = new CsvToBeanBuilder<Book>(new StringReader(data)).withType(Book.class).build().parse();   // RuntimeException: Error parsing CSV line: 2. [X,Y,abc], cause CsvDataTypeMismatchException

With withThrowExceptions(false), OpenCSV skips the bad rows and collects the errors, so the import can store the good rows and report the bad ones with their line numbers.

String data = "title,author,year\nX,Y,abc\nA,B,2001\n";
CsvToBean<Book> lenient = new CsvToBeanBuilder<Book>(new StringReader(data))
        .withType(Book.class)
        .withThrowExceptions(false)
        .build();
List<Book> good = lenient.parse();
int imported = good.size();                                           // 1
long badLine = lenient.getCapturedExceptions().get(0).getLineNumber(); // 2
String reason = lenient.getCapturedExceptions().get(0).getMessage();   // "Conversion of abc to int failed."

8. Creating CSV from SQL Data

Exporting a database table is a common task, and CSVWriter.writeAll(ResultSet, includeHeaders) writes all rows of a JDBC ResultSet. With includeHeaders set to true, the column labels become the first line.

static void exportBooks(Connection con, Path target) throws SQLException, IOException {
    try (Statement st = con.createStatement();
         ResultSet rs = st.executeQuery("SELECT title, author, published FROM book");
         CSVWriter writer = new CSVWriter(Files.newBufferedWriter(target))) {
        writer.writeAll(rs, true);
    }
}

Dates and timestamps are formatted with default patterns, which ResultSetHelperService.setDateFormat() and setDateTimeFormat() change. For millions of rows, we set a JDBC fetch size, so the driver does not load the whole result into memory first.

9. OpenCSV vs Other Ways to Parse CSV

Java has no CSV parser in the JDK. The CSV parsing overview compares the options in detail, and the short version is in this table.

OptionQuotes and embedded commasBeansGood for
String.split(“,”)NoNoSimple data we control, see splitting a CSV string
OpenCSVYesYes, with annotationsImports and exports with typed beans
Apache Commons CSVYesNoFast record-based parsing with named formats such as CSVFormat.EXCEL
Jackson CSV (jackson-dataformat-csv)YesYes, including recordsApps that already use Jackson for JSON

We pick OpenCSV when we want beans with annotations and little setup, and Commons CSV when we want a small, fast parser without bean mapping. For batch jobs, Spring Batch has its own FlatFileItemReader for CSV.

10. OpenCSV FAQs

The bean annotations and the quoting rules raise more questions than reading or writing itself.

10.1. Does OpenCSV support Java records?

No. CsvToBeanBuilder creates beans with a public no-argument constructor and sets the fields afterward, so a record fails with CsvBeanIntrospectionException. Jackson’s CSV module supports records, and with OpenCSV we can read into maps or a bean and convert.

10.2. How do I skip the header line?

With CSVReader, we use new CSVReaderBuilder(reader).withSkipLines(1). The bean API with @CsvBindByName uses the header for the mapping, so it skips the header without any setting.

10.3. How do I parse dates with OpenCSV?

We add @CsvDate(“yyyy-MM-dd”) next to the binding annotation on a LocalDate, LocalDateTime or Date field. Without @CsvDate, a date field fails with CsvDataTypeMismatchException.

10.4. Why does CSVWriter put quotes around every value?

Quoting all fields is the default, which is valid CSV and safe for any content. To quote only values that contain the separator, a quote or a line break, we call writeNext(line, false) or use withApplyQuotesToAll(false) on StatefulBeanToCsvBuilder.

10.5. Is OpenCSV RFC 4180 compliant?

The default CSVParser handles nearly all real files and treats a backslash as an escape character. For files that must follow RFC 4180 strictly, including backslashes as plain text, we use new RFC4180ParserBuilder().build() with withCSVParser().

11. Conclusion

OpenCSV reads CSV files as arrays, maps or beans and writes them back with correct quoting. The builders, namely CSVReaderBuilder, CsvToBeanBuilder and StatefulBeanToCsvBuilder, replace the older setter-based API and cover separators, skipped lines, column order and error collection.

For large files, we iterate instead of calling readAll() or parse(), and for messy data we collect the bad rows with withThrowExceptions(false). The bean API needs classic beans, so records go through a small adapter class.

12. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. I am new to Selenium Webdriver Testng and trying to Develop Data Driven framework using CSVReader

    I have 5 tests in Testng and have to run those all on different environments. So basically I have to pick a different url and client, but other pages, elements remain same.

    My Question is, Where do I put while loop so that it reads first line and execute all the tests and then read second line and so on.

    I tried initialize CSV reader at class level, before suite, before method etc. but it throws error, it works for a particular test if I am putting insside @Test but that doesn’t resolve the purpose, I couldn’t find any relevant answer on the internet.

  2. Please note that for Android, mapping to a java bean does not work due to Android missing the class “java.beans.Introspector”. You can use reader.readAll() and then take it from there.

  3. CSVReader reader = new CSVReader(new FileReader(“data.csv”), ‘,’ , ‘”‘ , 1);

    reader.readNext() doesn’t work properly when one of the columns data has special characters as input such as below

    “~!@#$%^&*()_+`[]{}|:””;,./{}|{}|+*&%#@~\”

  4. Hi,
    I don’t see in this tutorial and can’t find on the web any example of usage of BeanToCsv class.
    Can someone provide this ?
    Thanks

  5. Good tutorial, seems pretty clearly organized. I would however, have 2 questions for you based on this material:
    1. Would it possible to apply the same mapping from CSV directly to Java bean in case of having non-String fields? For example if the employee ID and the Employee age were declared integers, would the same mapping presented here be valid?
    2. How would it be possible to export data using OpenCSV into HTML format?

  6. what is the parse method in class CSVMappedToJavaBeanExample.java as it is not defined in Employee class.
    List list = csv.parse(setColumMapping(), csvReader);

  7. Hello Sir,

    This information is very useful. However, in my requirement, I need to ignore the occurrence of delimiter in the last string I get from CSV. Can you give an example how it could be done?

    Thanks & regards,
    Santhosh Kumar.

      • First Name;;; Last Name; City; Address;; zip code;; and this \n is the \n additional; text; as \n a last string

        Thank you for your reply Lokesh. Consider the above row as text in the CSV file. In this case, using “;” as delimiter, I can get the strings. But in the last string mentioned in brackets, (and this \n is the \n additional; text; as \n a last string), I dont want the delimition to happen. This is what I am looking for. In this case, I cannot use “\” as escape character as \n would not work. Please help. Thanks in advance

        • You can do only those things with code which you logically implement in steps. In your case, probably you first need to use the delimiter ‘and’ or any such word. This will divide you string into two parts. You can again tokenize the first part with delimiter ‘;’ and leave the second part.

          If you can not break your logic into logical steps, you will not be able to write the code for it as well. So please try to create logical steps first, and then let me know for which part you want me to help in writing code.

          I hope I make sense.

  8. Hi,

    I would like to write mutiple resultsets into a csv file. How can I do that? Is there anyway I can write in next tabs?

    Please advice.

    Thanks,
    Sivaprasath

  9. Sir,

    If my bean class has reference of other Class then how do I bind csv to my bean class, consider following example :

    public class Employee{
    private int empId;
    private int empName;
    private Address address;

    }

    public class Address {
    private String city;
    private String state;
    }

    And my Employee.csv file is like this :

    EmpId,EmpName,City,State
    101,xyz,abc,def

    Please clarify

    • Apache POI was never designed to call on CSV files. While a CSV File may be opened in Excel, Excel has its own reader that does an auto import. For CSV file, OpenCSV is better option.

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.