Montag, 12. Januar 2009
Zeilen einer CSV-Datei splitten
Wie kann man eine Zeile einer CSV-Datei splitten? Ganz einfach, mit der String-Methode 'split'.

Nun das Problem: Die String-Werte sind in Anführungszeichen eingeschlossen und können auch das Trennzeichen als normalen Text enthalten. Die 'split'-Methode kann die vermeintlichen Trennzeichen in dem normalen Text nicht übersehen. Eine Lösung bietet meine CsvSplitter Klasse:

public class CsvSplitter
{
// public static void main(String[] args) {
// CsvSplitter sp = new CsvSplitter(';', '"');
// System.out.println(Arrays.toString(sp.split("a;b;cde;\"ha;ha; you never expected this\";test")));
// System.out.println(Arrays.toString(sp.split("a;b;cde;")));
// System.out.println(Arrays.toString(sp.split("ade")));
// System.out.println(Arrays.toString(sp.split("")));
// System.out.println(Arrays.toString(sp.split("\"as\"f\";")));
// }

public CsvSplitter(char delimiter, char quote) {
this.delimiter = delimiter;
this.quote = quote;
}

public String[] split(String s) {
if (s.length() == 0) {
return new String[] { "" };
}
text = s;
init();
int tokenCount = 0;
while (nextToken()) {
tokenCount++;
}
String result[] = new String[tokenCount];

init();
int i = 0;
while (nextToken()) {
result[i++] = s.substring(startIdx, endIdx);
}
return result;
}

private char delimiter;

private char quote;

private String text;

private int length;

private boolean quoteOpened;

private int startIdx;

private int endIdx;

private int textIdx;


private void init() {
length = text.length();
quoteOpened = false;
startIdx = 0;
endIdx = 0;
textIdx = 0;
}

/**
* calculates startIdx and endIdx of next token
*/
private boolean nextToken() {
if (textIdx > 0) {
if (textIdx == length && endIdx < length && text.charAt(endIdx) == delimiter) { // empty token
endIdx = startIdx;
return true;
}
else if (textIdx >= length) {
return false; // no more token
}
startIdx = endIdx + 1; // index of next token
}
boolean tokenFound = false;
while (!tokenFound) {
char ch = text.charAt(textIdx);
if (ch == quote) {
if (quoteOpened) {
if (textIdx + 2 < length) {
quoteOpened = text.charAt(textIdx + 1) != delimiter;
}
else {
quoteOpened = false;
}
}
else {
quoteOpened = true;
}
}
else if (!quoteOpened && ch == delimiter) {
endIdx = textIdx;
tokenFound = true;
}
else if (textIdx == length - 1) {
endIdx = textIdx + 1;
tokenFound = true;
}
textIdx++;
}
return true;
}
}

... comment