CSV Parser with Quoted Fields

hard Files and I/O

Problem

Split every CSV line into fields. A comma separates fields, except inside double quotes. A field in double quotes can contain commas, and two double quotes in a row mean one quote character. The surrounding quotes are not part of the field. Print the fields of each line joined by the | character.

Constraints

  • 1 <= lines <= 10^4

Examples

Input

a,b,c
"hello, world",x

Output

a|b|c
hello, world|x

Solutions

Best approachTime O(total length)Space O(line length)

Splitting on commas is not enough for real CSV files. A small state machine that remembers whether we are inside quotes handles commas and doubled quotes. In real projects use a tested CSV library.

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * Problem : Process lines.
 * Input   : Several lines.
 * Output  : Fields joined by |.
 * Approach: CSV lines.
 * Time    : O(total length)
 * Space   : O(line length)
 * Java    : 8 or newer.
 */
import java.util.Scanner;

public class Main {
        // Step 1: Split one line with a small state machine. "inQuotes" tells if we are inside a quoted field.
        static String parseLine(String line) {
            java.util.List<String> fields = new java.util.ArrayList<>();
            StringBuilder current = new StringBuilder();
            boolean inQuotes = false;
            for (int i = 0; i < line.length(); i++) {
                char c = line.charAt(i);
                if (inQuotes) {
                    // Step 2: Inside quotes, two quotes in a row mean one quote. A single quote ends the field text.
                    if (c == '"' && i + 1 < line.length() && line.charAt(i + 1) == '"') {
                        current.append('"');
                        i++;
                    } else if (c == '"') {
                        inQuotes = false;
                    } else {
                        current.append(c);
                    }
                } else if (c == '"') {
                    inQuotes = true;
                } else if (c == ',') {
                    // Step 3: A comma outside quotes ends the field.
                    fields.add(current.toString());
                    current.setLength(0);
                } else {
                    current.append(c);
                }
            }
            fields.add(current.toString());
            return String.join("|", fields);
        }

    // This method solves the task. It receives the input and returns the answer.
    static String[] solve(String[] lines) {
        String[] output = new String[lines.length];
        for (int i = 0; i < lines.length; i++) {
            output[i] = parseLine(lines[i]);
        }
        return output;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        java.util.List<String> all = new java.util.ArrayList<>();
        while (scanner.hasNextLine()) {
            all.add(scanner.nextLine());
        }
        String[] lines = all.toArray(new String[0]);
        System.out.println(String.join("\n", solve(lines)));
    }
}
Hints (1)
  1. Why does line.split(',') fail on quoted fields?