Initial commit

This commit is contained in:
Tilman
2011-10-26 08:38:22 +02:00
commit 4aa81b24ae
9 changed files with 767 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
"""Always forgive your enemies; nothing annoys them so much.""", Oscar Wilde
"Zweite Zeile", völlig in, Ordnung
"Escaped Chars\"", "in Quoted Fields \\", und \, außerhalb
Spaces , und Tabs
#Kommentarzeile, gefolgt von leerer Zeile
"#feld mit", "#Kommentarzeichen", #in den Feldern
"field", "with
CR inside"
test1, te"st2, test3
#"fehlerhafte", "Zeile
#"gefolgt von", "korrekter Zeile"
+11
View File
@@ -0,0 +1,11 @@
"Buchungsdatum";"Valuta";"Betrag";"Waehrung";"Empfaengername1";"Empfaengername2";"Primanota";"BLZ Empfaenger";"Kontonummer Empfaenger";"Verwendungszweck 1";"Verwendungszweck 2"
"27.07.05";"27.07.05";-29,00;"EUR";"KARTENSERVICE";"";"999010";"10050000";"5231240003";"VISA NR. 4555787019929887";"ABRECHNUNG VOM 22.07"
"29.07.05";"29.07.05";-51,13;"EUR";"BHW BAUSPARKASSE HAMELN";"";"999010";"25410200";"471862300";"BHW 0471862 3 00 K CBA-05";""
"01.08.05";"01.08.05";200,00;"EUR";"BAEUERLE-WALTHER,ERIKA";"";"999101";"10050000";"1320184258";"UNTERHALT";""
"01.08.05";"01.08.05";115,00;"EUR";"WALTHER-STRATMANN,SIEGMUND";"";"853001";"10050000";"710150091";"UNTERHALT AUGUST 2005";""
"01.08.05";"31.07.05";-20,00;"EUR";"";"";"954011";"10050000";"5310031253";"GA NR00001253 BLZ10050000 9";"31.07/09.23UHR FIL. 125"
"02.08.05";"02.08.05";-13,20;"EUR";"CALL A PIZZA, BERLIN";"";"999011";"37010050";"555619501";"EC 68005963 30.07 19.56 ME9";""
"02.08.05";"02.08.05";-25,32;"EUR";"VODAFONE D2";"";"999010";"30050000";"6444111";"000010753003 000265200517 0";"RECHNUNGSNR 103397270879"
"gebuchter Saldo";"02.08.05";1574,22;"EUR"
"alter Saldo";"19.07.05";1397,87;"EUR"
+3
View File
@@ -0,0 +1,3 @@
Direktes Verarbeiten braucht Funktionalität eines Pull-Parsers:
while ((row = parser.getNextRow()) != null) useRow;
Weiterverarbeitung ins TableModel etc. wird von Wrappe-Klasse übernommen
+150
View File
@@ -0,0 +1,150 @@
package de.tilman.csvparser;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.LinkedList;
/**
* Repräsentiert eine CSV-Datei
*
* @author Tilman Walther
*/
public class CsvFile {
// TODO english comments
/* TODO andere Ausgabeformate.
* http://www.ricebridge.com/products/csvman/api/com/ricebridge/csvman/CsvManager.html
* The CsvManager class provides the following ways to load and save CSV data:
* # As a List of String[] arrays - load(File), save(File,List)
* # As a List of Lists of Strings - loadAsLists(File), saveAsLists(File,List)
* # As a ResultSet - loadResultSet(File,boolean), save(File,ResultSet,boolean)
* # As a TableModel - loadTableModel(File,boolean), save(File,TableModel,boolean)
* # From callback interfaces - LineListener, FieldListener, LineProvider
*/
CsvFileProperties properties;
CsvParser parser;
File csvFile;
int lineCounter = 0;
String[] header = null; // enthält die Tabellendefinition als Folge von Strings
LinkedList data; // enthält die Zeilen der Tabelle als String-Arrays
/**
* Der Konstruktor für den "default mode". Die CSV-Datei wird geöffnet und eingelesen, dabei
* werden folgende Annahmen gemacht:
* <ul>
* <li>Jede Zeile enthält genau einen Datensatz</li>
* <li>Trennzeichen zwischen Feldern ist , (Komma)</li>
* <li>Das Zeichen für Einkapselung ist " (Doppelte Anführungsstriche)</li>
* <li>Kommentarzeilen (mit #) sind nicht erlaubt</li>
* </ul>
*
* @param csvFile the CSV file
* @throws IOException
* @throws CsvParseException
*/
public CsvFile(File csvFile) throws IOException, CsvParseException {
properties = new CsvFileProperties();
//parser = new CsvLineParser(properties);
this.csvFile = csvFile;
readData();
}
public CsvFile(File csvFile, CsvFileProperties properties) throws IOException, CsvParseException {
this.properties = properties;
//parser = new CsvLineParser(properties);
this.csvFile = csvFile;
readData();
}
/**
* Gibt die Daten aus der CSV-Datei zurück. Die LinkedList enthält die
* Zeilen der Daten-Tabelle als String-Arrays.
* @return the data of the csv file
*/
public LinkedList getData() {
return data;
}
/**
* Gibt die Spaltendefinition (d.h. die erste Zeile der Daten-Tabelle) zurück.
* Falls die CSV-Datei laut CsvProperties keinen Header hat, wird null zurückgegeben.
* @return the definition of the data table columns
*/
public String[] getHeader() {
return header;
}
/**
* Liest die Daten aus der CSV-Datei
* @throws CsvParseException
* @throws Exception
*/
protected void readData() throws IOException, CsvParseException {
BufferedReader file;
try {
file = new BufferedReader(new InputStreamReader(new FileInputStream(csvFile)));
}
catch (FileNotFoundException fnfe) {
try {
file = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(csvFile.getAbsolutePath())));
}
catch (NullPointerException npe) {
throw new IOException("Die Datei '"+csvFile+"' konnte nicht geöffnet werden.");
}
}
CsvParser parser = new CsvParser(file, properties);
String line;
try
{
// Spaltendefinition einlesen
if (properties.headerInFirstLine()) {
header = parser.getNextRow();
if (header == null) {
throw new IOException("Die Datei '"+csvFile+"' enthält keine Daten.");
}
}
data = new LinkedList();
String[] row;
while (((row = parser.getNextRow()) != null) && (lineCounter < 50))
{
lineCounter++;
data.add(row);
}
//System.out.println("Line counter at "+lineCounter);
}
catch (IOException ioe)
{
throw ioe;
}
finally
{
try
{
file.close();
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
}
+171
View File
@@ -0,0 +1,171 @@
package de.tilman.csvparser;
/**
* Properties of a CSV file.
*
* @author Tilman Walther
*/
public class CsvFileProperties {
// TODO Comments
public static final char DEFAULT_SEPARATOR = ',';
public static final char DEFAULT_QUOTED_FIELD_MARKER = '"';
public static final char DEFAULT_COMMENT_LINE_MARKER = '#';
public static final char DEFAULT_ESCAPED_CHAR_MARKER = '\\';
/**
* true, if the first line is the table header
*/
private boolean headerInFirstLine;
/**
* the field separator character
*/
private char separator;
/**
* true, if fields can be wrapped in quotes
*/
private boolean quotedFields;
/**
* the character that wraps quoted fields
*/
private char quotedFieldMarker;
// private boolean doubleQuoteEscape;
private boolean commentLines;
private char commentLineMarker;
private boolean escapedChars;
private char escapedCharMarker;
private boolean ignoreEmptyLines;
/**
* Constructor for the default mode:
* <ul>
* <li>No header line</li>
* <li>Separator = CsvFileProperties.DEFAULT_SEPARATOR</li>
* <li>Quoted fields are allowed</li>
* <li>Quoted field marker = CsvFileProperties.DEFAULT_QUOTED_FIELD_MARKER</li>
* <li>Double quote escape allowed</li>
* <li>No comment lines</li>
* <li>No escaped characters</li>
* <li>No empty lines</li>
*/
public CsvFileProperties() {
headerInFirstLine = false;
separator = DEFAULT_SEPARATOR;
quotedFields = true;
quotedFieldMarker = DEFAULT_QUOTED_FIELD_MARKER;
// doubleQuoteEscape = true;
commentLines = false;
commentLineMarker = DEFAULT_COMMENT_LINE_MARKER;
escapedChars = false;
escapedCharMarker = DEFAULT_ESCAPED_CHAR_MARKER;
ignoreEmptyLines = false;
}
public CsvFileProperties(boolean headerInFirstLine, char separator,
boolean quotedFields, char quotedFieldMarker,
boolean doubleQuoteEscape, boolean commentLines,
char commentLineMarker, boolean escapedChars,
char escapedCharMarker, boolean ignoreEmptyLines) {
this.headerInFirstLine = headerInFirstLine;
this.separator = separator;
this.quotedFields = quotedFields;
this.quotedFieldMarker = quotedFieldMarker;
// this.doubleQuoteEscape = doubleQuoteEscape;
this.commentLines = commentLines;
this.commentLineMarker = commentLineMarker;
this.escapedChars = escapedChars;
this.escapedCharMarker = escapedCharMarker;
this.ignoreEmptyLines = ignoreEmptyLines;
}
public char getCommentLineMarker() {
return commentLineMarker;
}
public void setCommentLineMarker(char commentLineMarker) {
this.commentLineMarker = commentLineMarker;
}
public boolean commentLines() {
return commentLines;
}
public void setCommentLines(boolean commentLines) {
this.commentLines = commentLines;
}
// public boolean doubleQuoteEscape() {
// return doubleQuoteEscape;
// }
//
// public void setDoubleQuoteEscape(boolean doubleQuoteEscape) {
// this.doubleQuoteEscape = doubleQuoteEscape;
// }
public char getEscapedCharMarker() {
return escapedCharMarker;
}
public void setEscapedCharMarker(char escapedCharMarker) {
this.escapedCharMarker = escapedCharMarker;
}
public boolean escapedChars() {
return escapedChars;
}
public void setEscapedChars(boolean escapedChars) {
this.escapedChars = escapedChars;
}
public boolean headerInFirstLine() {
return headerInFirstLine;
}
public void setHeaderInFirstLine(boolean headerInFirstLine) {
this.headerInFirstLine = headerInFirstLine;
}
public boolean ignoreEmptyLines() {
return ignoreEmptyLines;
}
public void setIgnoreEmptyLines(boolean ignoreEmptyLines) {
this.ignoreEmptyLines = ignoreEmptyLines;
}
public char getQuotedFieldMarker() {
return quotedFieldMarker;
}
public void setQuotedFieldMarker(char quotedFieldMarker) {
this.quotedFieldMarker = quotedFieldMarker;
}
public boolean quotedFields() {
return quotedFields;
}
public void setQuotedFields(boolean quotedFields) {
this.quotedFields = quotedFields;
}
public char getSeparator() {
return separator;
}
public void setSeparator(char separator) {
this.separator = separator;
}
}
@@ -0,0 +1,35 @@
package de.tilman.csvparser;
/**
* @author Tilman Walther
*/
public class CsvParseException extends Exception {
int lineIndex = -1;
/**
* This is the constructor used for the first step of the exception handling.
* It stores the given column index. This index is used later while generating
* the message string of the (final) exception.
*
* @param lineIndex the index of the line where the parse error occured
*/
public CsvParseException(int lineIndex) {
this.lineIndex = lineIndex;
}
public int getLineIndex() {
return lineIndex;
}
// /*
// * This is the standard exception constructor.
// *
// * @param message
// */
// public CsvParseException(String message) {
// super(message);
// }
}
+285
View File
@@ -0,0 +1,285 @@
package de.tilman.csvparser;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
/**
* @author Tilman Walther
*/
public class CsvParser {
/*
* TODO Hält sich hoffentlich an alle Regeln
* http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm#FileFormat
* http://www.ricebridge.com/products/csvman/demo.htm
*/
/**
* the field separator character
*/
final char SEPARATOR;
/**
* true, if fields can be wrapped in quotes, e.g. in order to use a carriage return
* within fields. If false, the quote character is treated as a normal character.
*/
final boolean QUOTED_FIELDS;
/**
* the quoting character
*/
final char QUOTED_FIELD_MARKER;
/**
* true, if the CSV file can contain comment lines that shall be ignored by the parser
*/
final boolean COMMENTS;
/**
* the comment character. If a line begins with this character and COMMENTS == true, the
* line is ignored.
*/
final char COMMENT_LINE_MARKER;
/**
* if true, all characters that follow the escaped char marker are inserted into the
* field as they are
*/
final boolean ESCAPED_CHARS;
/**
* the escape character
*/
final char ESCAPED_CHAR_MARKER;
/**
* true, if ampty lines should be ignored by the parser. If false, empty lines are treated like
* a line of empty fields.
*/
final boolean IGNORE_EMPTY_LINES;
private final BufferedReader reader;
private final ArrayList row = new ArrayList(100);
private final StringBuffer field = new StringBuffer(100);
public CsvParser(BufferedReader reader, CsvFileProperties properties) {
SEPARATOR = properties.getSeparator();
QUOTED_FIELDS = properties.quotedFields();
QUOTED_FIELD_MARKER = properties.getQuotedFieldMarker();
COMMENTS = properties.commentLines();
COMMENT_LINE_MARKER = properties.getCommentLineMarker();
ESCAPED_CHARS = properties.escapedChars();
ESCAPED_CHAR_MARKER = properties.getEscapedCharMarker();
IGNORE_EMPTY_LINES = properties.ignoreEmptyLines();
this.reader = reader;
}
// true, if the current character is the first of the row (and not inside a quoted field)
private boolean newLine;
// true, if the pointer is behind a separator and the content of the field has not started
private boolean newField;
// true, if the first character in the field was a quotation mark
private boolean insideQuotedField;
// Number of successive spaces before the current character. (Spaces before a field's content aren't counted.)
private final ArrayList spaces = new ArrayList(50);
// true, if the preceding row was finished by a carriage return '\r'
private boolean terminatedByCR;
private int nextChar;
protected void insertSpacesBefore() {
// TODO ist spaces.size() performant?
for (int i = 0; i < spaces.size(); i++) field.append(spaces.get(i));
spaces.clear();
}
protected String[] getRowAsStringArray() {
// TODO was ist mit leeren Zeilen?
row.add(field.toString());
// TODO Einsatzgebiet für Java 5 Generics und row.toArray()?
final int rowLength = row.size();
String[] resArray = new String[rowLength];
for (int i = 0; i < rowLength; i++) {
resArray[i] = (String) row.get(i);
}
return resArray;
}
/**
* @return the next row of the table or null, if the end of the table is reached
* @throws CsvParseException
* @throws IOException
*/
public String[] getNextRow() throws CsvParseException, IOException {
newLine = true; // TODO wird newLine immer auf false gesetzt, wenn nötig? (Bis jetzt immer nach der IF-Behandlung in Zeile 210)
newField = true;
insideQuotedField = false;
spaces.clear();
terminatedByCR = false;
row.clear();
field.setLength(0);
nextChar = reader.read();
while (nextChar != -1) {
// TODO was ist schneller: Globales ch (Allokation) oder final ch (Zugriff/Caching)?
final char ch = (char) nextChar;
if (ch == SEPARATOR) {
if (!insideQuotedField) {
spaces.clear();
newField = true;
row.add(field.toString());
field.setLength(0);
}
else {
insertSpacesBefore();
field.append(ch);
}
}
else if ((ch == ' ') || (ch == '\t')) {
if (!newField) {
spaces.add(new Character(ch));
}
}
else if (ch == QUOTED_FIELD_MARKER) {
if (newField) {
newField = false;
insideQuotedField = true;
}
else if (insideQuotedField) {
nextChar = reader.read();
if (nextChar != -1) {
// double qoute escape?
char ch2 = (char) nextChar;
if (ch2 == QUOTED_FIELD_MARKER) {
insertSpacesBefore();
field.append(QUOTED_FIELD_MARKER);
}
else {
// XXX Hier wird sehr viel gemacht. Sicherlich optimierbar.
// Ab hier dürfen bis zum Ende des Feldes bzw. der Zeile nur
// noch Spaces oder der Separator folgen
insideQuotedField = false;
while ((nextChar != -1) && (ch2 != SEPARATOR) && (ch2 != '\r') && (ch2 != '\n')) {
nextChar = reader.read();
ch2 = (char) nextChar;
}
if (nextChar != -1) {
if (ch2 == SEPARATOR) {
spaces.clear();
newField = true;
row.add(field.toString());
field.setLength(0);
}
else if (ch2 == '\r') {
terminatedByCR = true;
return getRowAsStringArray();
}
else if (ch2 == '\n') {
return getRowAsStringArray();
}
}
}
}
}
else {
insertSpacesBefore();
field.append(ch);
}
}
else if (ch == '\r') {
if (!insideQuotedField) {
if ((!IGNORE_EMPTY_LINES) || (field.length() > 0)) {
// terminatedByCR wird true gesetzt, damit ein direkt folgenden '\n' nicht erneut als
// Zeilenumbruch interpretiert wird
terminatedByCR = true;
return getRowAsStringArray();
}
else {
spaces.clear();
newLine = true;
newField = true;
}
}
else {
field.append(ch);
}
}
else if (ch == '\n') {
if (!insideQuotedField) {
if ((!IGNORE_EMPTY_LINES) || (field.length() > 0)) {
if (!terminatedByCR) {
return getRowAsStringArray();
}
else {
terminatedByCR = false;
}
}
else {
spaces.clear();
newLine = true;
newField = true;
}
}
else {
field.append(ch);
}
}
else if (ESCAPED_CHARS && (ch == ESCAPED_CHAR_MARKER)) {
newField = false;
nextChar = reader.read();
if (nextChar != -1) {
insertSpacesBefore();
field.append((char) nextChar);
}
else {
// XXX der Parameter der CsvParseException muss korrekt sein
throw new CsvParseException(0);
}
}
else if (newLine && COMMENTS && (ch == COMMENT_LINE_MARKER)) {
do {
nextChar = reader.read();
} while ((nextChar != '\n') && (nextChar != '\r'));
if (nextChar == '\r') {
terminatedByCR = true;
}
else {
terminatedByCR = false;
}
}
else {
newField = false;
insertSpacesBefore();
field.append(ch);
}
newLine = false;
nextChar = reader.read();
}
// TODO falls noch eine Zeile im Puffer ist, erst diese zurückgeben
return null;
}
}
@@ -0,0 +1,90 @@
package de.tilman.csvparser.sample;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import de.tilman.csvparser.CsvFileProperties;
import de.tilman.csvparser.CsvParser;
public class SampleUsage {
public static void main(String[] args) throws Exception {
// TODO Pfad anpassen
BufferedReader file = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\Eigene Dateien\\eclipse workspace\\CSVparser\\de\\tilman\\csvparser\\sample\\csvdata.txt")));
CsvParser parser = new CsvParser(file, new CsvFileProperties(false, ',', true, '"', true, true, '#', true, '\\', true));
String[] row;
while ((row = parser.getNextRow()) != null) {
System.out.print(" | ");
for (int i = 0; i < row.length; i++) {
System.out.print(row[i]);
System.out.print(" | ");
}
System.out.println();
}
}
// public static String row2string(String[] fields, boolean print) {
// if (fields == null) return null;
// StringBuffer buf = new StringBuffer();
// for (int i = 0; i < (fields.length-1); i++) {
// if (print) System.out.print(fields[i]+" | ");
// buf.append(fields[i]+" | ");
// }
// if (print) System.out.println(fields[fields.length-1]);
// buf.append(fields[fields.length-1]);
// return buf.toString();
// }
//
// public static void main(String[] args) throws Exception {
// BufferedReader file = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\Eigene Dateien\\eclipse workspace\\CSVparser\\Testdaten.txt")));
//
// String[] resultStrings = {
// "\"Always forgive your enemies; nothing annoys them so much.\" | Oscar Wilde",
// "Zweite Zeile | völlig in | Ordnung",
// "Escaped Chars\" | in Quoted Fields \\ | und , außerhalb",
// "Spaces | und Tabs",
// "",
// "#feld mit | #Kommentarzeichen | #in den Feldern",
// "field | with\nCR inside",
// "test1 | te\"st2 | test3"
// };
//
// CsvParser parser = new CsvParser(file, new CsvFileProperties(false, ',', true, '"', true, true, '#', true, '\\', false));
// String result;
// int errCount = 0;
//
// for (int i = 0; i < resultStrings.length; i++) {
// System.out.println("\nIndex "+i);
// result = row2string(parser.getNextRow(), true);
// System.out.println(resultStrings[i]+" (check)");
// if (result == null) {
// if (resultStrings[i] == null) {
// System.out.println(" --> erfolgreich");
// }
// else {
// errCount++;
// System.out.println(" --> FEHLER!");
// }
// }
// else if (result.equals(resultStrings[i])) {
// System.out.println(" --> erfolgreich");
// }
// else {
// errCount++;
// System.out.println(" --> FEHLER!");
// }
// }
//
// if (errCount == 0) {
// System.out.println("\n\nAlle Tests erfolgreich durchgeführt.");
// }
// else {
// System.err.println("\n\n"+errCount+" Fehler aufgetreten");
// }
//
// }
}
+10
View File
@@ -0,0 +1,10 @@
# a CSV file with comment lines, empty lines and double quote escaping
"This", "is", "the", "first", "line"
This, is, the, second, line
"""I often quote myself. It adds spice to my conversation.""", "George Bernard Shaw", "(1850 - 1896)"
"This is a", "line with
CR", "inside a field"