Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - CSV-Datei lesen


GuaAck - Di 23.07.13 22:52
Titel: CSV-Datei lesen
Hallo,
ich möchte eine CSV-Datei auswerten, also eine Textdatei mit Zeilen der Art:

Wert1;Wert2;Wert3;

Ich kenne den Typ von Wert1, Wert2 und Wert 3.

Ich habe mal eine sehr schöne Funktion in Delphi dafür genutzt, leider habe ich die aber in der Hilfe und meinen Projekten nicht mehr finden können. Es ging im Prinzip so:

Trennzeichen_festlegen (';');
Lese(Wert1); {Liest bis ';' und setzt dann den Lesezeiger auf die Position nach dem nächsten ';'}
Lese(Wert2);
Lese(Wert3);

Vielleicht war es eine Funktion in der Stringverarbeitung, vielleicht auch im Bereich TStream, da habe ich aber nichts finden können.

Kennt jemand so eine Funktion?

(Klar, als Notlösung scanne ich den String einer Zeile selbst, zerlege ihn in seine Einzelteile und wandele z. B. mit strtoint, strtofloat usw. um. Ist aber umständlicher.)

Gruß GuaAck


OldGrumpy - Mi 24.07.13 00:14

Wenn es nicht allzu viele Daten sind und die Geschwindigkeit nicht zu kritisch ist, kann man TStringList dafür missbrauchen, zum Beispiel so:

- Zwei TStringList instantiieren.
- Ab (IIRC) D2005 unbedingt StrictDelimiter:=True setzen (war früher Default, ab der Version ist es umgekehrt), Delimiter auf Semikolon setzen.
- In die erste die CSV-Datei laden (LoadFromFile)
- In einer Schleife jeweils eines der Items der ersten TStringList als DelimitedText der zweiten StringList setzen. Und schon ist die CSV-Zeile getrennt.

Stößt allerdings an seine Grenzen, wenn das Trennzeichen auch innerhalb der Felder vorkommt - die Tauglichkeit hängt also von den Daten ab. Ein fertiges Äquivalent zu explode() aus PHP beispielsweise habe ich aber auch schon mehrfach rumgehen sehen.


Sinspin - Mi 24.07.13 06:46

user profile iconOldGrumpy hat folgendes geschrieben Zum zitierten Posting springen:
Stößt allerdings an seine Grenzen, wenn das Trennzeichen auch innerhalb der Felder vorkommt - die Tauglichkeit hängt also von den Daten ab. Ein fertiges Äquivalent zu explode() aus PHP beispielsweise habe ich aber auch schon mehrfach rumgehen sehen.

CSV ist ein Standard für Tabellen im Textformat und kann damit umgehen das die Trennzeichen in den Strings enthalten sein können. Mit zwei TStringList ist eine CSV Datei recht komfortabel zu lesen. TStringList verarbeitet auch großen Dateien erstaunlich schnell.


jaenicke - Mi 24.07.13 10:20

Nebenbei, in XE4 ginge das auch so:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
uses
  System.SysUtils, System.IOUtils;

var
  CurrentLine: string;
  CurrentLineValues: TArray<string>;
begin
  for CurrentLine in TFile.ReadAllLines('blubb.csv'do
  begin
    CurrentLineValues := CurrentLine.Split([';']);
    // CurrentLineValues[0]...
  end;
Aber ich vermute mal anhand deiner Profilangabe, dass du das nicht nutzen kannst.

Für echte CSV-Dateien ist ein entsprechender Reader aber sinnvoller.


Xion - Mi 24.07.13 10:37

user profile iconSinspin hat folgendes geschrieben Zum zitierten Posting springen:
CSV ist ein Standard für Tabellen im Textformat und kann damit umgehen das die Trennzeichen in den Strings enthalten sein können.

CSV kann das, aber der vorgeschlagene Algorithmus eben nicht. Zum Beispiel:


Quelltext
1:
1,"""Siehe da"", sagte er",14                    


Dann kommt man nicht mehr weit, wenn man nur bis zum nächsten Komma kopiert ;)

Es hängt natürlich ganz davon ab, woher diese CSV kommt, ob sowas überhaupt vorkommt. Sind alle Spalten z.B. Integer, dann sollte es keine Probleme damit geben. Da die Zeilen aber gegen jeden Standard durch ";" getrennt werden, vermute ich, dass da eine Excel-Tabelle dahinter steckt.

Man bräuchte wohl so eine Art Zustandsmaschine. Lese bis zum nächsten Trennzeichen oder ". Wenn " gelesen und es folgt noch eins, dann wird es durch ein " ersetzt. Sonst beginnt hier ein String und endet erst beim nächsten einfachen ", Trennzeichen dazwischen sind also zu ingorieren. Immer wenn man dann ein Trennzeichen gefunden hat, schneidet man das Stück ab und gibt es zurück.
Wenn man Typen kennt, kann man die gleich prüfen und eine vernünftige Fehlerbehandlung durchführen.


Wenn man auf das alles verzichtet, dann sieht die einfachste Variante wohl in etwa so aus:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
var csv: TextFile; S: String;
AssignFile(csv,path+'my.csv');
Reset(csv);
while not EoF(csv) do
  begin
    ReadLn(csv,S);
    S:=S+';';
    while Pos(';',S)>0 do
      begin
         Spalte := Copy(S,1,Pos(';',S)-1);
         S := Copy(S,Pos(';',S)+1,MaxInt);
      end;       
  end;
CloseFile(csv);


jaenicke - Mi 24.07.13 10:49

Noch einfacher ist aber gleich einen fertigen Reader zu benutzen wie diesen hier...
http://www.delphipraxis.net/110025-csv-reader-schnelles-lesen-von-csv-dateien.html


MeierZwoo - Mi 24.07.13 11:57

user profile iconSinspin hat folgendes geschrieben Zum zitierten Posting springen:
CSV ist ein Standard für Tabellen im Textformat und kann damit umgehen das die Trennzeichen in den Strings enthalten sein können.

Wie unterscheidet dann wer (?) Trennzeichen und Trennzeichen innerhalb eines Strings?

mfg


Xion - Mi 24.07.13 13:22

Texte werden üblicherweise mit " umgeben. Bsp:

1,"Apfel",15

Steht jetzt ein Trennzeichen im String ist das kein Problem, denn es ist eindeutig, dass dieses zum String gehört:

1,"Apfel,Birne",15

Einziges Problem dabei ist, wenn ebenfalls ein " im String vorkommt, weshalb diese immer escaped werden (bei csv doppelt notiert)

Statt
1,"51° 14′ 4,2"",15
heißt es also:
1,"51° 14′ 4,2""",15

Der Parser kennt das Prinzip und weiß zum einen, dass "" nichts mit der Begrenzung des Strings zu tun hat, und dass er "" danach wieder durch " ersetzen muss.

Das ist eigentlich so üblich, wobei es scheinbar kein genormtes CSV-Format gibt. An die Anführungszeichen halten sich wohl noch die meisten, das Trennzeichen selbst ist meist verschieden. Comma Seperated Values gibt also auch mit Strichpunkt oder Leerzeichen getrennt. Und dann gibt es natürlich sehr viele Codes, die das mit den Anführungszeichen nicht richtig berücksichtigen, sowohl beim Import als auch beim Export ;)


MeierZwoo - Mi 24.07.13 18:21

.. danke. Ähnlich wie bei SQL mit den doppelten Tüddelchen innerhalb eines mit Tüddelchen umgrenzten Stringalterals.