It may be simpler to code in terms of single fields, instead of lines or "records" of CSV fields. Code sample below uses Java8 streams to build up a result; however, it's trivial to convert it to Java7 using an ArrayList<String>
to build up the results.
package test;
import java.util.stream.Stream;
public class CSV {
public static String[] split(final String text) {
return (text.indexOf('"') < 0)? text.split(",")
: splitCSV(Stream.<String>builder(), text, 0).build().toArray(String[]::new);
}
private static Stream.Builder<String> splitCSV(final Stream.Builder<String> accum, final String text, int start) {
final int length = text.length();
if (start >= length) return accum;
final StringBuilder buf = new StringBuilder(length);
boolean inquote = false;
if (text.charAt(start) == '"') {
inquote = true;
start++;
}
int i = start;
loop:
for (; i < length; i++) {
final char c = text.charAt(i);
switch (c) {
case '"':
if (inquote) {
if (i + 1 < length && text.charAt(i + 1) == '"') {
i++;
break;
}
else {
inquote = false;
continue loop;
}
}
break;
case '\\':
if (i + 1 < length) {
final char next = text.charAt(i + 1);
switch (next) {
case 'b': buf.append('\b'); i++; continue loop;
case 'f': buf.append('\f'); i++; continue loop;
case 'r': buf.append('\r'); i++; continue loop;
case 'n': buf.append('\n'); i++; continue loop;
case 't': buf.append('\t'); i++; continue loop;
case '"': buf.append('"'); i++; continue loop;
}
}
break;
case ',':
if (!inquote) {
// value termination
i++;
break loop;
}
break;
}
buf.append(c);
}
return splitCSV(accum.add(buf.toString()), text, i);
}
}
I tested it briefly using the Scala interpreter:
scala> import test.CSV
import test.CSV
scala> CSV.split("N/A,N\\A,ThirdField")
res0: Array[String] = Array(N/A, N\A, ThirdField)
scala> CSV.split(""""A quoted value","One with ""embedded"" quotes",One without,"",a,""")
res1: Array[String] = Array(A quoted value, One with "embedded" quotes, One without, "", a)
scala> res1(3)
res2: String = ""
scala> res2.length
res3: Int = 0
scala> CSV.split("""First,second,"third, with a comma",fourth has a \nnewline,"fifth:\tbeing tricky, has ""some real"" testing craziness!"
| """
| )
res4: Array[String] =
Array(First, second, third, with a comma, fourth has a
newline, "fifth: being tricky, has "some real" testing craziness!
")
scala> CSV.split("""One,second has \"escaped\" quotes,third""")
res5: Array[String] = Array(One, second has "escaped" quotes, third)
Test Notes:
1. Scala has triple-quoted strings that allow embedded single quotes, newlines, and backslashes, etc., without escaping.
2. The Scala interpreter prints empty strings as a set of empty quotes (""
), but non-empty strings without surrounding quotes.