CSV-Datei einlesen

Für Fragen von Einsteigern und Programmieranfängern...
vlenzer
Beiträge: 7
Registriert: Fr 23. Dez 2016, 12:17

CSV-Datei einlesen

Beitrag von vlenzer »

Hallo zusammen,

ich arbeite momentan an einem Programm welches eine CSV-Datei mit 3 Spalten und einer ungewissen Anzahl an Zeilen (bis zu 1*10^10) behandeln können soll.
In den Spalten stehen X-, Y- und Z-Koordinaten getrennt per Semikolon. Die Größe der Datei wird laut meiner Übeschlagungsrechnung mehrere GB haben. Leider habe ich noch keine entsprechende Datei vorliegen.

Nun muss ich Berechnungen mit den X- und Y-Koordinaten durchführen (Koordinatentransformation). Später möchte ich die neuen Koordinaten in eine neue CSV Datei schreiben.

Meine erste Überlegung zum groben Ablauf: Ich lese die X und Y Werte in 2 Verschiedene Arrays ein und führe dann die Berechnungen durch. Zum Schluss möchte ich diese per for Schleife und writeln (x[i],";",y[i],";",z[i]); in eine neue CSV-Datei schreiben.

Ich bin jetzt allerdings schon so weit, dass ich statt Arrays mit TStringLists arbeiten sollte. Abgesehen von der Geschwindigkeit.

Meine Probleme:
[*]Gibt es eine einfache Möglichkeit das Abspeichern zu ermöglichen? Wie Genau speichere ich die 1. Spalte in TStringList1, 2. Spalte in TStringList2 etc.
[*]Der Rechenaufwand wird sehr lang, oder? Kann ich das irgendwie noch beeinflussen? Soll ich doch lieber mit normalen Arrays arbeiten?

Ich hoffe, dass ich an alles gedacht habe. Falls Fragen sind, einfach Fragen :roll:

Viele Grüße und schon mal ein schönes Fest wünschend
vlenzer

wp_xyz
Beiträge: 4892
Registriert: Fr 8. Apr 2011, 09:01

Re: CSV-Datei einlesen

Beitrag von wp_xyz »

Was genau wird da berechnet? Reicht für die Berechnung einer neuen Zeile die Information der entsprechenden alten Zeile aus, oder brauchst du dafür alle Zeilen? Im ersten Fall würde ich auf keinen Fall die Datei komplett einlesen, sondern Zeile für Zeile, die Transformation durchführen und das Ergebnis gleich wieder in die Zieldatei schreiben. Dafür brauchst du nur elementare Kenntnisse, wie man eine Text-Datei liest, keine StringList, usw.

Im zweiten Fall wird's schwierig. Ohne näheres darüber zu wissen, möchte ich hier auch keine Ratschläge geben.

vlenzer
Beiträge: 7
Registriert: Fr 23. Dez 2016, 12:17

Re: CSV-Datei einlesen

Beitrag von vlenzer »

wp_xyz hat geschrieben:Was genau wird da berechnet? Reicht für die Berechnung einer neuen Zeile die Information der entsprechenden alten Zeile aus, oder brauchst du dafür alle Zeilen?

Die Berechnung kann auch Zeilenweise geschehen, daran habe ich noch garnicht gedacht. Ich muss die Werte nur mit einem vorher bestimmten Variablen multiplizieren. Zu beachten ist hier, dass nur die X- und Y-Koordinate neue Werte zugewiesen bekommt. Die Z-Koordinate bleibt gleich und kann einfach übernommen werden. Die vorher berechneten Variablen für X und Y sind voneinander verschieden.
wp_xyz hat geschrieben:Dafür brauchst du nur elementare Kenntnisse, wie man eine Text-Datei liest, keine StringList, usw.

Mein Problem ist dabei nur, dass ich diese elementaren Kenntnisse scheinbar nict habe :oops: Wie kann ich auf die einzelnen Einträge pro Zeile zugreifen? Hast Du da ein Stichwort?

wp_xyz
Beiträge: 4892
Registriert: Fr 8. Apr 2011, 09:01

Re: CSV-Datei einlesen

Beitrag von wp_xyz »

Wo fehlt's denn?

Bei Einlesen der Datei? --> z.B. viewtopic.php?f=9&t=3796
Beim Extrahieren der x,y,z-Werte aus den Zeilen? Z.B. viewtopic.php?f=10&t=9083&p=80279&hilit=zeile+zerlegen#p80279
Beim Speichern? Z.B http://forum.lazarus.freepascal.org/ind ... l#msg50184

Mathias
Beiträge: 6204
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: CSV-Datei einlesen

Beitrag von Mathias »

Dies wäre ein einfacher Lösungsweg.

Code: Alles auswählen

type
  TVec3 = array[0..2] of single;
  TVec3Arr = array of TVec3;
 
procedure TForm1.Button1Click(Sender: TObject);
var
  sl: TStringList;
  sa: TStringArray;
 
  vecarr: TVec3Arr;
  l, i, j: integer;
begin
  sl := TStringList.Create;
  sl.LoadFromFile('/home/tux/test/test2.csv');
 
  SetLength(vecarr, sl.Count);
 
  for j := 0 to sl.Count - 1 do begin
    sa := sl[j].Split(';');
    for i := 0 to 2 do begin
      vecarr[j, i] := StrToFloat(sa[i]);
    end;
  end;
 
  //  Mache etwas mit der vecarr
 
  for j := 0 to Length(vecarr) - 1 do begin   // Testausgabe
    Memo1.Lines.Add(FloatToStr(vecarr[j, 0]) + ';' + FloatToStr(vecarr[j, 1]) + ';' + FloatToStr(vecarr[j, 2]));
  end;
  Memo1.Lines.SaveToFile('neueDatei.svn');
 
  sl.Free;
end


Kleine test svn:

Code: Alles auswählen

1;2;3
4;5;6
7;8;9
10;11;12


Die Größe der Datei wird laut meiner Übeschlagungsrechnung mehrere GB haben.

Wen ich aber dies hier lese, denke ich wäre es besser Zeile einlesen, abarbeiten speichern, nächste Zeile einlesen usw.
Dann würde ich mit einem klassischem ReadLn und WriteLn arbeiten und auf StringList verzichten.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

vlenzer
Beiträge: 7
Registriert: Fr 23. Dez 2016, 12:17

Re: CSV-Datei einlesen

Beitrag von vlenzer »

wp_xyz hat geschrieben:Wo fehlt's denn?

Bei Einlesen der Datei? --> z.B. viewtopic.php?f=9&t=3796
Beim Extrahieren der x,y,z-Werte aus den Zeilen? Z.B. viewtopic.php?f=10&t=9083&p=80279&hilit=zeile+zerlegen#p80279
Beim Speichern? Z.B http://forum.lazarus.freepascal.org/ind ... l#msg50184

Nur beim spaltenweisen Extrahieren. Wenn ich deinen Link richtig verstehe: Zeile als String einlesen und einzelne Einträge als Zahlenwerte extrahieren?

Mathias hat geschrieben:Wen ich aber dies hier lese, denke ich wäre es besser Zeile einlesen, abarbeiten speichern, nächste Zeile einlesen usw.
Dann würde ich mit einem klassischem ReadLn und WriteLn arbeiten und auf StringList verzichten.

Per klassischem Readln? Damit bekomme ich es doch nicht hin, die Spaltenwerte zu separieren, oder?

Mathias
Beiträge: 6204
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: CSV-Datei einlesen

Beitrag von Mathias »

In den Spalten stehen X-, Y- und Z-Koordinaten getrennt per Semikolon.

Wen ich das lese dachte ich die svn sehe so aus:

Code: Alles auswählen

X;Y;Z
X;Y;Z
X;Y;Z
X;Y;Z

 
Oder ist die Datei etwa so:

Code: Alles auswählen

X;X;X;X;X
Y;Y;Y;Y;Y
Z;Z;Z;Z;Z
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

vlenzer
Beiträge: 7
Registriert: Fr 23. Dez 2016, 12:17

Re: CSV-Datei einlesen

Beitrag von vlenzer »

Mathias hat geschrieben:
In den Spalten stehen X-, Y- und Z-Koordinaten getrennt per Semikolon.

Wen ich das lese dachte ich die svn sehe so aus:

Code: Alles auswählen

X;Y;Z
X;Y;Z
X;Y;Z
X;Y;Z


Ne, so ist es richtig. Aber wenn ich die erste Zeile einlese, muss ich ja mit den X und Y die Berechnung durchführen. Die Spalten also separieren

Code: Alles auswählen

 
1;Y´1;Z1                                                                        X1;Y1;Z1
2;Y´2;Z2    ---Berechnung ([i]*a und Y´[i]*b)-->                              X2;Y2;Z2
3;Y´3;Z3                                                                        X3;Y3;Z3


Und ich weiß nicht, wie ich das hinbekomme. Weder mit einem einfachen readln, noch anders :(

Mathias
Beiträge: 6204
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: CSV-Datei einlesen

Beitrag von Mathias »

Meinst du so was ?
Die Test-SVN ist immer noch die gleiche wie oben.

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
const
  a = 1.23;
  b = 4.56;
var
  fq, fz: Text;
  s: string;
  sa: TStringArray;
  vec3: array[0..2] of single;
  i: integer;
begin
  AssignFile(fq, '/home/tux/test/test2.csv');
  AssignFile(fz, '/home/tux/test/testausgabe.csv');
  Reset(fq);
  Rewrite(fz);
  while not EOF(fq) do begin
    ReadLn(fq, s);
    sa := s.Split(';');
    for i := 0 to 2 do begin
      vec3[i] := StrToFloat(sa[i]);
    end;
    vec3[0] := vec3[0] * a;  // X
    vec3[1] := vec3[1] * a;  // Y
    WriteLn(fz, vec3[0]:4:2, ';', vec3[1]:4:2, ';', vec3[2]:4:2);
  end;
  CloseFile(fq);
  CloseFile(fz);
 
  Memo1.Lines.LoadFromFile('/home/tux/test/testausgabe.csv')// Test Voransicht
end;


Noch eine Frage, ist der Dezimaltrenner ein '.' oder ein ',' ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

wp_xyz
Beiträge: 4892
Registriert: Fr 8. Apr 2011, 09:01

Re: CSV-Datei einlesen

Beitrag von wp_xyz »

Mathias hat geschrieben:Meinst du so was ?
Die Test-SVN ist immer noch die gleiche wie oben.

Code: Alles auswählen

    sa := s.Split(';');

Zeig ihm doch nicht sowas. Der OP ist Anfänger, und man sollte ihm vor allem nicht zumuten, fpc-trunk zu installieren.

Am besten sieht man, was passiert, wenn man die Aufgabe Schritt für Schritt ganz klassisch durchgeht:

  • Führe einen Hilfsstring ein (tmp), der jeweils den Teilstring bis zum Trennzeichen aufnehmen soll.
  • Beginne mit tmp := '' (Leerstring)
  • Beginnend beim 1.Zeichen, durchlaufe den zu zerteilenden String (s) Zeichen für Zeichen (for i := 1 to Length(s) do...).
  • Prüfe, ob das aktuelle Zeichen ein Strichpunkt ist (if s[i] = ';').
  • Wenn nein, hängst du die aktuelle Zeichen an den Hilfsstring an (tmp := tmp + s[i]).
  • Wenn ja, hast du die erste Zahl (x) gefunden und kannst sie als Double-Variable x abspeichern (x := StrToFloat(tmp)). Setze anschließend den Hilfsstring wieder auf leer (tmp := ''), um dasselbe Spielchen für y zu wiederholen.
  • Also: setze das Durchlaufen von s fort, bis du den nächsten Strichpunkt findest - nun hast du y.
  • Nach dem nächsten Zyklus ist der String zu Ende und tmp enhält die Stringdarstellung von z.
Sicher, es gibt fertige Routinen, z.b. mit einer Stringlist. Aber wenn ein Anfänger so elementare Aufgaben nicht hinkriegt, bleibt Programmieren für ihn immer eine Hexerei.

P.S.
vlenzer, ich habe in der Beschreibung angenommen, dass jede Zeile aufgebaut ist wie z.B. "1,234;2,567;3,3487". Falls, wie oben angedeutet, zusätzlich noch Bezeichner X', Y', Z' vorhanden sind (also "X'1,234;Y'2,567;Z'3,3487"), musst du das Verfahren entsprechend anpassen. Das ist sicher kein Problem, wenn du das Prinzip verstanden hast.
Zuletzt geändert von wp_xyz am Fr 23. Dez 2016, 22:33, insgesamt 1-mal geändert.

vlenzer
Beiträge: 7
Registriert: Fr 23. Dez 2016, 12:17

Re: CSV-Datei einlesen

Beitrag von vlenzer »

Mathias hat geschrieben:Meinst du so was ?
...
Noch eine Frage, ist der Dezimaltrenner ein '.' oder ein ',' ?

Trennzeichen ist ein Komma.
Ich schau es mir morgen mal an. Danke schon mal!
wp_xyz hat geschrieben:Zeig ihm doch nicht sowas. Der OP ist Anfänger, und man sollte ihm vor allem nicht zumuten, fpc-trunk zu installieren.

Am besten sieht man, was passiert, wenn man die Aufgabe Schritt für Schritt ganz klassisch durchgeht:

Vielen Dank für deine Liste. Da werde ich mich versuchen dran zu halten. Mal sehen, was ich selbst auf die Beine gestellt bekomme. Hört sich aber so an, als könne ich was damit anfangen.

Ich wünsche euch nochmals schöne Feiertage!

Edit:
wp_xyz hat geschrieben:P.S.
vlenzer, ich habe in der Beschreibung angenommen, dass jede Zeile aufgebaut ist wie z.B. "1,234;2,567;3,3487". Falls, wie oben angedeutet, zusätzlich noch Bezeichner X', Y', Z' vorhanden sind (also "X'1,234;Y'2,567;Z'3,3487"), musst du das Verfahren entsprechend anpassen. Das ist sicher kein Problem, wenn du das Prinzip verstanden hast.

Nein, nein. Mit den Bezeichnungen X´ etc. wollte ich nur den Wert vor der Berechnung verdeutlichen (X[i]=a*X´[i]). Das Format ist in der Tat "1,234;2,567;3,3487". Also alles gut :wink:

Mathias
Beiträge: 6204
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: CSV-Datei einlesen

Beitrag von Mathias »

Zeig ihm doch nicht sowas. Der OP ist Anfänger, und man sollte ihm vor allem nicht zumuten, fpc-trunk zu installieren.

Da habe ich leider nicht daran gedacht, das dies immer noch der Trunk vorbehalten ist. :oops:

Wie lange wird es noch gehen, bis 3.2 veröffentlicht wird ?
Immerhin hat es da paar recht gute Sachen drinnen.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

vlenzer
Beiträge: 7
Registriert: Fr 23. Dez 2016, 12:17

Re: CSV-Datei einlesen

Beitrag von vlenzer »

Achso. Eine Frage doch noch. Ist die Herangehensweise mit dem temporären String nicht sehr rechen- und damit zeitintensiv? Vor allem, wenn meine CSV Datei so groß wird?

wp_xyz
Beiträge: 4892
Registriert: Fr 8. Apr 2011, 09:01

Re: CSV-Datei einlesen

Beitrag von wp_xyz »

Warum arbeitest du überhaupt mit fpc-trunk? Wenn sich da ein Fehler einschleicht, hast du erheblich mehr Aufwand, wieder ein lauffähiges System zu bekommen als mit Lazarus Trunk (fpc-trunk neu zu kompilieren ist immer ein Abenteuer, zumindest für mich). Und die neuen Features? Sie machen abhängig von dieser Version und man kann nicht mehr zu stable zurück. Für mich sind das Gimmicks für Leute, die von anderen Sprachen kommen und die Syntax dieser Sprachen auch in Pascal haben möchten (genau sowas wie das "s.Split", oder "s.Length" eben). Und wenn man Komponenten schreibt, sind die Neuerungen wegen der Inkompatibilität zu älteren Versionen sowieso tabu. Ich selbst verwende fpc-trunk nur, um gefixte fpc-Bugs zu testen, die ich gemeldet habe.

Mathias
Beiträge: 6204
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: CSV-Datei einlesen

Beitrag von Mathias »

Etwas anderes wird es kaum geben, da die csv selbst auch ein String ist.
Wen dein Daten binär vorliegen würden, dann ginge alles natürlich schneller.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten