This comprehensive guide covers various methods for parsing and generating CSV files in Java, including native approaches and popular libraries.
1. Native Java CSV Processing
Basic CSV Reader
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class NativeCsvParser {
public List<String[]> parseCsv(String filePath) throws IOException {
List<String[]> records = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = br.readLine()) != null) {
String[] values = parseCsvLine(line);
records.add(values);
}
}
return records;
}
private String[] parseCsvLine(String line) {
List<String> values = new ArrayList<>();
StringBuilder currentValue = new StringBuilder();
boolean inQuotes = false;
for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i);
if (c == '"') {
// Handle escaped quotes ("")
if (inQuotes && i < line.length() - 1 && line.charAt(i + 1) == '"') {
currentValue.append('"');
i++; // Skip next quote
} else {
inQuotes = !inQuotes;
}
} else if (c == ',' && !inQuotes) {
values.add(currentValue.toString());
currentValue.setLength(0);
} else {
currentValue.append(c);
}
}
values.add(currentValue.toString());
return values.toArray(new String[0]);
}
// Usage example
public static void main(String[] args) {
NativeCsvParser parser = new NativeCsvParser();
try {
List<String[]> data = parser.parseCsv("data.csv");
for (String[] row : data) {
System.out.println(String.join(" | ", row));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Basic CSV Writer
import java.io.*;
import java.util.List;
public class NativeCsvWriter {
public void writeCsv(String filePath, List<String[]> data) throws IOException {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(filePath))) {
for (String[] row : data) {
bw.write(convertToCsvRow(row));
bw.newLine();
}
}
}
private String convertToCsvRow(String[] row) {
StringBuilder csvRow = new StringBuilder();
for (int i = 0; i < row.length; i++) {
String value = row[i];
// Check if value needs quoting
if (value.contains(",") || value.contains("\"") || value.contains("\n")) {
// Escape quotes and wrap in quotes
value = "\"" + value.replace("\"", "\"\"") + "\"";
}
csvRow.append(value);
if (i < row.length - 1) {
csvRow.append(",");
}
}
return csvRow.toString();
}
// Usage example
public static void main(String[] args) {
NativeCsvWriter writer = new NativeCsvWriter();
List<String[]> data = List.of(
new String[]{"Name", "Age", "City"},
new String[]{"John Doe", "30", "New York"},
new String[]{"Jane Smith", "25", "San Francisco, CA"},
new String[]{"Bob \"The Builder\"", "40", "Chicago"}
);
try {
writer.writeCsv("output.csv", data);
System.out.println("CSV file written successfully");
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. Apache Commons CSV
Dependencies
<!-- Maven --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-csv</artifactId> <version>1.10.0</version> </dependency> <!-- Gradle --> implementation 'org.apache.commons:commons-csv:1.10.0'
CSV Parsing with Apache Commons CSV
import org.apache.commons.csv.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class CommonsCsvParser {
public List<Person> parseCsvToObjects(String filePath) throws IOException {
List<Person> people = new ArrayList<>();
try (Reader reader = new FileReader(filePath);
CSVParser parser = new CSVParser(reader, CSVFormat.DEFAULT
.withFirstRecordAsHeader()
.withIgnoreHeaderCase()
.withTrim())) {
for (CSVRecord record : parser) {
Person person = new Person();
person.setName(record.get("Name"));
person.setAge(Integer.parseInt(record.get("Age")));
person.setEmail(record.get("Email"));
person.setCity(record.get("City"));
people.add(person);
}
}
return people;
}
public void parseCsvWithCustomFormat(String filePath) throws IOException {
CSVFormat format = CSVFormat.newFormat(';') // Custom delimiter
.withQuote('"')
.withHeader("Name", "Age", "Email", "City")
.withIgnoreEmptyLines()
.withIgnoreSurroundingSpaces();
try (Reader reader = new InputStreamReader(
new FileInputStream(filePath), StandardCharsets.UTF_8);
CSVParser parser = new CSVParser(reader, format)) {
for (CSVRecord record : parser) {
System.out.printf("Name: %s, Age: %s, Email: %s%n",
record.get("Name"), record.get("Age"), record.get("Email"));
}
}
}
public void parseLargeCsvFile(String filePath) throws IOException {
try (Reader reader = new FileReader(filePath);
CSVParser parser = CSVFormat.DEFAULT.parse(reader)) {
int rowCount = 0;
for (CSVRecord record : parser) {
// Process each record
processRecord(record);
rowCount++;
if (rowCount % 1000 == 0) {
System.out.println("Processed " + rowCount + " records");
}
}
}
}
private void processRecord(CSVRecord record) {
// Custom record processing logic
String[] values = new String[record.size()];
for (int i = 0; i < record.size(); i++) {
values[i] = record.get(i);
}
// Process the values...
}
}
// Model class
class Person {
private String name;
private int age;
private String email;
private String city;
// Constructors, getters, and setters
public Person() {}
public Person(String name, int age, String email, String city) {
this.name = name;
this.age = age;
this.email = email;
this.city = city;
}
// Getters and setters...
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
@Override
public String toString() {
return String.format("Person{name='%s', age=%d, email='%s', city='%s'}",
name, age, email, city);
}
}
CSV Generation with Apache Commons CSV
import org.apache.commons.csv.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
public class CommonsCsvWriter {
public void writeObjectsToCsv(List<Person> people, String filePath) throws IOException {
CSVFormat format = CSVFormat.DEFAULT
.withHeader("Name", "Age", "Email", "City")
.withQuote('"');
try (Writer writer = new FileWriter(filePath);
CSVPrinter printer = new CSVPrinter(writer, format)) {
for (Person person : people) {
printer.printRecord(
person.getName(),
person.getAge(),
person.getEmail(),
person.getCity()
);
}
printer.flush();
}
}
public void writeCsvWithCustomFormat(String filePath) throws IOException {
CSVFormat format = CSVFormat.newFormat('|') // Pipe delimiter
.withQuote('"')
.withHeader("ID", "Product", "Price", "Category")
.withNullString("N/A")
.withIgnoreEmptyLines();
try (Writer writer = new OutputStreamWriter(
new FileOutputStream(filePath), StandardCharsets.UTF_8);
CSVPrinter printer = new CSVPrinter(writer, format)) {
// Sample data
printer.printRecord(1, "Laptop", 999.99, "Electronics");
printer.printRecord(2, "Desk Chair", 199.50, "Furniture");
printer.printRecord(3, null, 49.99, "Accessories"); // Will be "N/A"
printer.printRecord(4, "Monitor", 299.99, "Electronics");
printer.flush();
}
}
public void generateCsvReport(List<SalesRecord> sales, String filePath) throws IOException {
CSVFormat format = CSVFormat.DEFAULT
.withHeader("Date", "Product", "Quantity", "Unit Price", "Total")
.withQuote('"');
try (Writer writer = new FileWriter(filePath);
CSVPrinter printer = new CSVPrinter(writer, format)) {
double grandTotal = 0;
for (SalesRecord sale : sales) {
double total = sale.getQuantity() * sale.getUnitPrice();
grandTotal += total;
printer.printRecord(
sale.getDate(),
sale.getProduct(),
sale.getQuantity(),
String.format("$%.2f", sale.getUnitPrice()),
String.format("$%.2f", total)
);
}
// Add summary row
printer.printRecord("", "", "", "Grand Total:", String.format("$%.2f", grandTotal));
printer.flush();
}
}
}
class SalesRecord {
private String date;
private String product;
private int quantity;
private double unitPrice;
// Constructors, getters, and setters
public SalesRecord(String date, String product, int quantity, double unitPrice) {
this.date = date;
this.product = product;
this.quantity = quantity;
this.unitPrice = unitPrice;
}
// Getters and setters...
public String getDate() { return date; }
public void setDate(String date) { this.date = date; }
public String getProduct() { return product; }
public void setProduct(String product) { this.product = product; }
public int getQuantity() { return quantity; }
public void setQuantity(int quantity) { this.quantity = quantity; }
public double getUnitPrice() { return unitPrice; }
public void setUnitPrice(double unitPrice) { this.unitPrice = unitPrice; }
}
3. OpenCSV Library
Dependencies
<!-- Maven --> <dependency> <groupId>com.opencsv</groupId> <artifactId>opencsv</artifactId> <version>5.8</version> </dependency> <!-- Gradle --> implementation 'com.opencsv:opencsv:5.8'
CSV Parsing with OpenCSV
import com.opencsv.*;
import com.opencsv.bean.*;
import com.opencsv.exceptions.CsvException;
import java.io.*;
import java.util.List;
public class OpenCsvParser {
public List<String[]> readAllData(String filePath) throws IOException, CsvException {
try (CSVReader reader = new CSVReader(new FileReader(filePath))) {
return reader.readAll();
}
}
public void readCsvLineByLine(String filePath) throws IOException {
try (CSVReader reader = new CSVReader(new FileReader(filePath))) {
String[] nextLine;
while ((nextLine = reader.readNext()) != null) {
// Process each line
System.out.println(String.join(" | ", nextLine));
}
}
}
// Using CsvToBean for object mapping
public List<Employee> parseToObjects(String filePath) throws IOException {
try (Reader reader = new FileReader(filePath)) {
CsvToBean<Employee> csvToBean = new CsvToBeanBuilder<Employee>(reader)
.withType(Employee.class)
.withIgnoreLeadingWhiteSpace(true)
.withSeparator(',')
.build();
return csvToBean.parse();
}
}
// Custom CSV parser with special settings
public void parseWithCustomSettings(String filePath) throws IOException, CsvException {
CSVParser parser = new CSVParserBuilder()
.withSeparator(';')
.withIgnoreQuotations(false)
.build();
CSVReaderBuilder readerBuilder = new CSVReaderBuilder(new FileReader(filePath))
.withCSVParser(parser)
.withSkipLines(1); // Skip header
try (CSVReader reader = readerBuilder.build()) {
List<String[]> allData = reader.readAll();
for (String[] row : allData) {
System.out.println(String.join(" | ", row));
}
}
}
}
// Employee model with CSV annotations
class Employee {
@CsvBindByName(column = "EMP_ID")
private String employeeId;
@CsvBindByName(column = "FIRST_NAME")
private String firstName;
@CsvBindByName(column = "LAST_NAME")
private String lastName;
@CsvBindByName(column = "DEPARTMENT")
private String department;
@CsvBindByName(column = "SALARY")
private double salary;
@CsvDate(value = "yyyy-MM-dd")
@CsvBindByName(column = "HIRE_DATE")
private Date hireDate;
// Constructors, getters, and setters
public Employee() {}
// Getters and setters...
public String getEmployeeId() { return employeeId; }
public void setEmployeeId(String employeeId) { this.employeeId = employeeId; }
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
public String getDepartment() { return department; }
public void setDepartment(String department) { this.department = department; }
public double getSalary() { return salary; }
public void setSalary(double salary) { this.salary = salary; }
public Date getHireDate() { return hireDate; }
public void setHireDate(Date hireDate) { this.hireDate = hireDate; }
@Override
public String toString() {
return String.format("Employee{id='%s', name='%s %s', department='%s', salary=%.2f}",
employeeId, firstName, lastName, department, salary);
}
}
CSV Generation with OpenCSV
import com.opencsv.*;
import com.opencsv.bean.*;
import java.io.*;
import java.util.Arrays;
import java.util.List;
public class OpenCsvWriter {
public void writeDataToCsv(List<String[]> data, String filePath) throws IOException {
try (CSVWriter writer = new CSVWriter(new FileWriter(filePath))) {
writer.writeAll(data);
}
}
public void writeObjectsToCsv(List<Employee> employees, String filePath) throws IOException {
try (Writer writer = new FileWriter(filePath)) {
StatefulBeanToCsv<Employee> beanToCsv = new StatefulBeanToCsvBuilder<Employee>(writer)
.withSeparator(',')
.withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
.build();
beanToCsv.write(employees);
} catch (CsvRequiredFieldEmptyException | CsvDataTypeMismatchException e) {
throw new IOException("Error writing CSV", e);
}
}
public void writeWithCustomSettings(String filePath) throws IOException {
CSVWriterBuilder writerBuilder = new CSVWriterBuilder(new FileWriter(filePath))
.withSeparator('|')
.withQuoteChar(CSVWriter.NO_QUOTE_CHARACTER)
.withEscapeChar(CSVWriter.DEFAULT_ESCAPE_CHARACTER)
.withLineEnd(CSVWriter.DEFAULT_LINE_END);
try (CSVWriter writer = writerBuilder.build()) {
// Write header
writer.writeNext(new String[]{"ID", "Product", "Price", "In Stock"});
// Write data
writer.writeNext(new String[]{"1", "Laptop", "999.99", "true"});
writer.writeNext(new String[]{"2", "Mouse", "29.99", "false"});
writer.writeNext(new String[]{"3", "Keyboard", "79.99", "true"});
writer.flush();
}
}
}
4. Advanced CSV Processing
CSV Processor with Validation
import java.io.*;
import java.util.*;
import java.util.function.Function;
public class AdvancedCsvProcessor {
public <T> List<T> processCsvWithValidation(
String filePath,
Function<String[], Optional<T>> mapper,
Function<String[], Boolean> validator) throws IOException {
List<T> results = new ArrayList<>();
List<String> errors = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
int lineNumber = 0;
while ((line = br.readLine()) != null) {
lineNumber++;
try {
String[] fields = parseCsvLine(line);
// Validate the record
if (!validator.apply(fields)) {
errors.add("Line " + lineNumber + ": Validation failed");
continue;
}
// Map to object
Optional<T> result = mapper.apply(fields);
result.ifPresent(results::add);
} catch (Exception e) {
errors.add("Line " + lineNumber + ": " + e.getMessage());
}
}
}
// Log errors
if (!errors.isEmpty()) {
System.err.println("Processing completed with errors:");
errors.forEach(System.err::println);
}
return results;
}
public void processLargeCsvFile(String inputPath, String outputPath,
Function<String[], String[]> transformer) throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader(inputPath));
BufferedWriter writer = new BufferedWriter(new FileWriter(outputPath))) {
String line;
boolean isHeader = true;
while ((line = reader.readLine()) != null) {
String[] fields = parseCsvLine(line);
if (isHeader) {
// Write header as-is
writer.write(line);
writer.newLine();
isHeader = false;
} else {
// Transform data
String[] transformed = transformer.apply(fields);
writer.write(convertToCsvRow(transformed));
writer.newLine();
}
}
}
}
private String[] parseCsvLine(String line) {
// Simplified CSV parsing - use a library in production
return line.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");
}
private String convertToCsvRow(String[] row) {
return String.join(",", Arrays.stream(row)
.map(field -> {
if (field.contains(",") || field.contains("\"")) {
return "\"" + field.replace("\"", "\"\"") + "\"";
}
return field;
})
.toArray(String[]::new));
}
}
CSV to JSON Converter
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.io.*;
import java.util.*;
public class CsvToJsonConverter {
private final ObjectMapper objectMapper;
public CsvToJsonConverter() {
this.objectMapper = new ObjectMapper();
this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
}
public void convertCsvToJson(String csvFilePath, String jsonFilePath) throws IOException {
List<Map<String, String>> jsonData = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(csvFilePath))) {
String line;
String[] headers = null;
while ((line = br.readLine()) != null) {
String[] fields = parseCsvLine(line);
if (headers == null) {
headers = fields;
} else {
Map<String, String> record = new LinkedHashMap<>();
for (int i = 0; i < headers.length && i < fields.length; i++) {
record.put(headers[i], fields[i]);
}
jsonData.add(record);
}
}
}
// Write JSON
try (FileWriter writer = new FileWriter(jsonFilePath)) {
objectMapper.writeValue(writer, jsonData);
}
}
private String[] parseCsvLine(String line) {
List<String> fields = new ArrayList<>();
StringBuilder field = new StringBuilder();
boolean inQuotes = false;
for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i);
if (c == '"') {
inQuotes = !inQuotes;
} else if (c == ',' && !inQuotes) {
fields.add(field.toString());
field.setLength(0);
} else {
field.append(c);
}
}
fields.add(field.toString());
return fields.toArray(new String[0]);
}
}
5. Spring Boot CSV Integration
Spring Boot CSV Controller
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.http.ResponseEntity;
import java.io.*;
import java.util.List;
@RestController
@RequestMapping("/api/csv")
public class CsvController {
private final CsvProcessingService csvProcessingService;
public CsvController(CsvProcessingService csvProcessingService) {
this.csvProcessingService = csvProcessingService;
}
@PostMapping("/upload")
public ResponseEntity<String> uploadCsv(@RequestParam("file") MultipartFile file) {
try {
List<Employee> employees = csvProcessingService.processCsvFile(file);
return ResponseEntity.ok("Processed " + employees.size() + " records");
} catch (IOException e) {
return ResponseEntity.badRequest().body("Error processing file: " + e.getMessage());
}
}
@GetMapping("/download")
public ResponseEntity<byte[]> downloadCsv() throws IOException {
byte[] csvData = csvProcessingService.generateCsvReport();
return ResponseEntity.ok()
.header("Content-Type", "text/csv")
.header("Content-Disposition", "attachment; filename=employees.csv")
.body(csvData);
}
}
@Service
public class CsvProcessingService {
public List<Employee> processCsvFile(MultipartFile file) throws IOException {
try (Reader reader = new InputStreamReader(file.getInputStream())) {
CsvToBean<Employee> csvToBean = new CsvToBeanBuilder<Employee>(reader)
.withType(Employee.class)
.withIgnoreLeadingWhiteSpace(true)
.build();
return csvToBean.parse();
}
}
public byte[] generateCsvReport() throws IOException {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Writer writer = new OutputStreamWriter(outputStream);
CSVPrinter printer = new CSVPrinter(writer, CSVFormat.DEFAULT
.withHeader("ID", "Name", "Department", "Salary"))) {
// Sample data - replace with actual data from database
printer.printRecord("1", "John Doe", "Engineering", "75000");
printer.printRecord("2", "Jane Smith", "Marketing", "65000");
printer.printRecord("3", "Bob Johnson", "Sales", "70000");
printer.flush();
return outputStream.toByteArray();
}
}
}
Best Practices Summary
- Use Established Libraries: Prefer Apache Commons CSV or OpenCSV over manual parsing
- Handle Character Encoding: Always specify encoding (UTF-8 recommended)
- Validate Input Data: Implement validation for CSV data
- Use Streaming for Large Files: Process large files line by line
- Handle Exceptions Gracefully: Provide meaningful error messages
- Consider Performance: Use appropriate data structures and avoid unnecessary object creation
- Test Edge Cases: Test with various CSV formats and edge cases
This comprehensive guide provides multiple approaches for CSV parsing and generation in Java, from basic implementations to advanced processing with popular libraries.