CSV-Problem

lambda

Erfahrenes Mitglied
Hi Zusammen,

ich bekomme Datensätze im folgendem Format:

"TEXT,aaa,bbb,ccc","TEXT2",1111,"TEXT3", "",12345,""

Relevant ist, das die einzelnen Werte also durch , (Komma) getrennt sind, aber Text-Werte in " " stehen und selber wieder , (Kommas) beinhalten können. Zahlen jedoch stehen nicht in " ".
Wobei natürlich die Reihenfolge wie in meinem Beispiel-String unterschiedlich sein kann.

Den String in den Tokenizer zu geben, klappt also nicht, weil ich

a) kein eindeutiges Trennzeichen habe (Zahlen nicht in " ")
b) in Texten wieder , (Komma) vorkommen


Meine Frage ist jetzt, muss ich selber einen Algorithmus schreiben der die einzelnen Werte trennt oder gibt es da eine vorgegebene, elegantere Möglichkeit?

Danke vorab!
 
Hab mal auf die Schnelle eine Lösung "zu Fuß" erstellt:
Code:
import java.util.ArrayList;
import java.util.ListIterator;

public class Tutorials
{

  public static void main(String[] args)
  {
    String csv = "\"TEXT,aaa,bbb,ccc\",\"TEXT2\",1111,\"TEXT3\",\"\",12345,\"\"";
    
    ArrayList test = specialSplit(csv);
    
    ListIterator it = test.listIterator();
    while (it.hasNext())
    {
      System.out.println((String)it.next());    
    }
    
  }
  
  public static ArrayList specialSplit(String special)
  {
    ArrayList result = new ArrayList();
    StringBuffer buffer = new StringBuffer(); 
    boolean stringOpen = false;
    
    for (int i=0; i<special.length(); i++)
    {
      if ("\"".equals(Character.toString(special.charAt(i))))
      {
        stringOpen = !stringOpen;
      }
      else
      {
        if (",".equals(Character.toString(special.charAt(i))) && !stringOpen)
        {
          result.add(buffer.toString());
          buffer = new StringBuffer();
        }
        else
        {
          buffer.append(special.charAt(i));
        }
      }
    }
    
    result.add(buffer.toString());
    return result;
  }
}
Kommentare hab ich mir aber gespart, da ich eigentlich gerade gar nicht die Zeit habe, sowas zu machen ;). Ist sicher auch noch verbesserungsfähig. Erklärungen folgen bei Bedarf.

Peter
 
@ ADA&QS: Schaue ich mir grade noch an... aber danke!

@ Peter:

Danke für den Code. Also ziehst du die Lösung "per pedes" :p vor? Ging mir eigentlich hauptsächlich um diese Aussage!

Dann noch ne Frage, warum benutzt du zum Anfügen den StringBuffer und nich einen normalen String?

Gruß, Peter#2
 
Hi Peter#2, hab wohl nicht genau gelesen... :)

Vorziehen würde ich nicht direkt sagen. Wenn's sich per Regex sauber splitten ließe, umso besser. Ich habe auch zuerst nach einem regulären Ausdruck gesucht, der bei String.split passend wäre. Dabei ist dann allerdings nur [\d"],[\d"] als Delimiter herausgekommen, was nur den Fehler hat, dass damit eine Zahl vor oder nach dem Komma mit "entsorgt" wird, weil sie ja Teil des Delimiters wird. Vielleicht hat da ja jemand eine passende Idee.

Den StringBuffer habe ich benutzt, da dieser grundsätzlich zum Zusammensetzen von Strings benutzt werden sollte. String-Objekte sind nicht veränderbar. Einmal erzeugt, bleiben sie so wie sie sind. Eine neue Zuweisung einer festen Zeichenkette bedeutet immer das Erzeugen eines neuen String-Objektes (sonst wäre z.B. auch eine Zuweisung einer Zeichenkette an ein String-Objekt ohne verherige initialisierung per new String() nicht möglich).

Deshalb wird auch beim Zusammensetzen von Strings per + Operator intern ein StringBuffer-Objekt benutzt. So wird intern z.B. aus String beispiel = "1" + 2 + '3'; eigentlich das hier: String beispiel = new StringBuffer().append("1").append(2).append('3').toString();.

Würde ich jetzt in meinem Beispiel nicht von vorneherein mit einem StringBuffer Objekt arbeiten, würde bei jedem Buchstaben, der angehängt wird ein neues StringBuffer- und ein neues String-Objekt erzeugt werden, was bei großen Mengen an Zeichen nicht unerheblich mehr Zeit in Anspruch nehmen dürfte.

Peter
 
Super Antwort! Danke das du dir die Zeit genommen hast.

Mit der RegExp wäre natürlich interessant.
 
Hi, wenn's noch relevant ist, dann könnte dir der reguläre Ausdruck weiterhelfen:
Code:
(?![\d"]),(?=[\d"])
Damit werden korrekt nur die Kommata deren Vorgänger und Nachfolger jeweils eine Zahl oder ein Anführungszeichen ist als Trennzeichen benutzt. Allerdings bleiben dabei auch die Anführungszeichen erhalten. Die kann man aber auch noch nachträglich rausschmeißen, wenn sie nicht erwünscht sind.

Hier mal ein kleines Implementierungsbeispiel, das die gleiche Ausgabe erzeugt, wie mein Weg "zu Fuß" von oben:
Code:
    String csv = "\"TEXT,aaa,bbb,ccc\",\"TEXT2\",1111,\"TEXT3\",\"\",12345,\"\"";
    
    String[] test = csv.split("(?![\\d\"]),(?=[\\d\"])");
            
    for(int i=0; i<test.length; i++)
    {
      test[i] = test[i].replaceAll("\"", "");
      System.out.println(test[i]);
    }
Peter
 
Zurück