[Gelöst] Welche Unit/Komponente für CSV Dateien
[Gelöst] Welche Unit/Komponente für CSV Dateien
Hallo,
Ich bin neu hier
Meine Object Pascal Programmiererfahrung liegt sehr sehr weit zurück. Die letzte Version mit der ich gearbeitet habe war Delphi 4.
Danach hat es mich beruflich in eine andere Richtung verschlagen und ich bin aus der Programmiererecke komplett raus gekommen...
Vor kurzem habe ich ein kleine Aufgabe übernommen und ich dachte mir ich könnte wieder etwas machen was mir immer Spaß gemacht hat: coden
Also sitze ich wieder hier gehe (wieder) durch eine steile Lernkurve...und es macht echt Spaß
Aber jetzt zu meiner Frage:
Die Aufgabe beinhaltet das einlesen von CSV Dateien (ca 5000-7000 Datensätze, einfache Summenbildung/Validierung über bestimmte Felder und das erstellen von CSV Dateien mit bestimmten Feldformaten.
Bei der Suche nach Lösungen/Beispielen und Doku's bin ich über CSVDocument und csvreadwrite gestolpert.
Leider habe ich jetzt noch nicht verstanden wie diese Komponenten einzuordnen sind und welche für mein Vorhaben am besten/einfachsten einzusetzen sind...
(Mein Plan ist es die Datensätze in ein Stringliste einzulesen (im Speicher halten) und zwecks Verarbeitung auf eine Record Struktur aufzubrechen, zu verarbeiten und anschließend die Sätze wieder in eine Stringliste zu schreiben und von da in eine CSV Datei zu streamen...)
Oder wäre es besser hier ein TStringGrid zu verwenden... (Eine Bearbeitung der Felder per GUI findet nicht statt und ich sehe das Thema Performance auch nicht sehr kritisch)
Sorry wenn die Frage sehr allgemein ist - ich stehe (wieder mal) am Anfang
Über Hinweise auf Doku's/Tipps/Beispiele würde ich mich sehr freuen
Gruß
Ich bin neu hier
Meine Object Pascal Programmiererfahrung liegt sehr sehr weit zurück. Die letzte Version mit der ich gearbeitet habe war Delphi 4.
Danach hat es mich beruflich in eine andere Richtung verschlagen und ich bin aus der Programmiererecke komplett raus gekommen...
Vor kurzem habe ich ein kleine Aufgabe übernommen und ich dachte mir ich könnte wieder etwas machen was mir immer Spaß gemacht hat: coden
Also sitze ich wieder hier gehe (wieder) durch eine steile Lernkurve...und es macht echt Spaß
Aber jetzt zu meiner Frage:
Die Aufgabe beinhaltet das einlesen von CSV Dateien (ca 5000-7000 Datensätze, einfache Summenbildung/Validierung über bestimmte Felder und das erstellen von CSV Dateien mit bestimmten Feldformaten.
Bei der Suche nach Lösungen/Beispielen und Doku's bin ich über CSVDocument und csvreadwrite gestolpert.
Leider habe ich jetzt noch nicht verstanden wie diese Komponenten einzuordnen sind und welche für mein Vorhaben am besten/einfachsten einzusetzen sind...
(Mein Plan ist es die Datensätze in ein Stringliste einzulesen (im Speicher halten) und zwecks Verarbeitung auf eine Record Struktur aufzubrechen, zu verarbeiten und anschließend die Sätze wieder in eine Stringliste zu schreiben und von da in eine CSV Datei zu streamen...)
Oder wäre es besser hier ein TStringGrid zu verwenden... (Eine Bearbeitung der Felder per GUI findet nicht statt und ich sehe das Thema Performance auch nicht sehr kritisch)
Sorry wenn die Frage sehr allgemein ist - ich stehe (wieder mal) am Anfang
Über Hinweise auf Doku's/Tipps/Beispiele würde ich mich sehr freuen
Gruß
Zuletzt geändert von BitRausch am Mi 9. Aug 2017, 13:16, insgesamt 1-mal geändert.
Re: Welche Unit/Komponente für CSV Dateien
Wie man mit dem CSV-Parser aus dem CSVdocument umgeht, findest du auf http://wiki.lazarus.freepascal.org/CsvD ... StringGrid (bin nicht 100% sicher, ob der Text aktuell ist, denn CSVdocument wurde vor einiger Zeit in fpc aufgenommen, und die Unit csvreadwrite wurde abgespalten, daher meine ich , dass du jetzt statt csvdocument die Unit csvreadwrite in "uses" aufführen musst).
Wenn dich primär interessiert, die Daten in einem StringGrid anzuzeigen, kannst auch dessen Methode LoadFromCSVFile (oder LoadFromCSVStream) verwenden.
Oder falls du die Zeilen selber zerlegen willst, kannst du alles in eine StringList (L) einlesen, diese String für String durchlaufen, und jeden String L[i] mit L[i].Split(<spaltentrenner>) in ein StringArray zerlegen (wobei der StringHelper .Split erst ab fpc 3.0 verfügbar ist - voher musst du selbst das Spaltentrennzeichen suchen und den String hier aufbrechen).
Und schließlich kannst du auch die Datenbank-Maschinerie anwerfen und die Datei in ein SdfDataset einlesen.
Viele Wege führen zum Ergebnis. Es kommt darauf an, was du genau machen willst.
Wenn dich primär interessiert, die Daten in einem StringGrid anzuzeigen, kannst auch dessen Methode LoadFromCSVFile (oder LoadFromCSVStream) verwenden.
Oder falls du die Zeilen selber zerlegen willst, kannst du alles in eine StringList (L) einlesen, diese String für String durchlaufen, und jeden String L[i] mit L[i].Split(<spaltentrenner>) in ein StringArray zerlegen (wobei der StringHelper .Split erst ab fpc 3.0 verfügbar ist - voher musst du selbst das Spaltentrennzeichen suchen und den String hier aufbrechen).
Und schließlich kannst du auch die Datenbank-Maschinerie anwerfen und die Datei in ein SdfDataset einlesen.
Viele Wege führen zum Ergebnis. Es kommt darauf an, was du genau machen willst.
Re: Welche Unit/Komponente für CSV Dateien
Vielen Dank für die sehr schnelle Antwort wp_xyz!!
Ich bin bei meiner Suche auch auf Deinen Beitrag vom letztem Jahr gestoßen ( viewtopic.php?f=55&t=10016&mobile=on) und da habe ich auch zum ersten mal gelesen das es überhaupt ein csvreadwrite gibt...Auslöser für diesen Thread
Dein Beispiel kommt meinem Vorhaben schon sehr nahe auch wenn es hier um die Ausgabe in eine CSV Datei geht...
Was ich nicht verstanden habe bzw. gefunden habe war
a) TCSVBuilder - wo finde ich Infos dazu
b) ob Dein Beispiel auch mit TStringlist geht statt mit TList
Im Bespiel auf der Wiki Seite wird mit TParser der String zerlegt...könnte ich diese Komponente auch nutzen?
Wo finde ich die Info dazu bzw. ist es in CSVDocument oder csvreadwrite?
(Vielleicht auch ein nützliche Info: Die Datensätze die eingelesen werden sollen haben eine Größe von 1200 Byte und bestehen aus 72 Feldern.)
Vielen Dank für Deine Unterstützung!
(Ich Versuche immer noch die vielen Info Schnipsel zusammen zu führen...)
Ich bin bei meiner Suche auch auf Deinen Beitrag vom letztem Jahr gestoßen ( viewtopic.php?f=55&t=10016&mobile=on) und da habe ich auch zum ersten mal gelesen das es überhaupt ein csvreadwrite gibt...Auslöser für diesen Thread
Dein Beispiel kommt meinem Vorhaben schon sehr nahe auch wenn es hier um die Ausgabe in eine CSV Datei geht...
Was ich nicht verstanden habe bzw. gefunden habe war
a) TCSVBuilder - wo finde ich Infos dazu
b) ob Dein Beispiel auch mit TStringlist geht statt mit TList
Im Bespiel auf der Wiki Seite wird mit TParser der String zerlegt...könnte ich diese Komponente auch nutzen?
Wo finde ich die Info dazu bzw. ist es in CSVDocument oder csvreadwrite?
(Vielleicht auch ein nützliche Info: Die Datensätze die eingelesen werden sollen haben eine Größe von 1200 Byte und bestehen aus 72 Feldern.)
Vielen Dank für Deine Unterstützung!
(Ich Versuche immer noch die vielen Info Schnipsel zusammen zu führen...)
Re: Welche Unit/Komponente für CSV Dateien
BitRausch hat geschrieben:a) TCSVBuilder - wo finde ich Infos dazu
Tut mir leid, aber besser als damals könnte ich das heute auch nicht schreiben. Ansonsten schaue dir den Quellcode von csvreadwrite an - immer noch besser als jede (unvollständige, nicht aktuelle) Dokumentation
BitRausch hat geschrieben:b) ob Dein Beispiel auch mit TStringlist geht statt mit TList
Ich versteh' nicht. Wenn deine Daten schon zu einer StringList zusammengefügt sind, hast du doch schon die CSV-Datei, du musst die Stringlist nur in eine Datei speichern (StringList.SaveToFile). Falls das anders ist, musst du mehr schreiben, was du genau machen willst. Sonst wird das nur Gefasel meinerseits.
Generell: TList hat bis auf das "List" im Namen und einige gleichnamige Methoden nichts mit TStringList gemeinsam. TStringList speichert primär Strings, TList speichert Pointer auf irgendwas, üblicherweise Pointer auf Records oder Objekte.
BitRausch hat geschrieben:Im Bespiel auf der Wiki Seite wird mit TParser der String zerlegt...könnte ich diese Komponente auch nutzen?
TParser? Das ist etwas anderes... In dem Beispiel wird TCSVParser verwendet. Der ist für das Lesen einer CSV-Datei zuständig und zerlegt die Datei (bestehend aus komma- oder anders-separierten Strings) in die einzelnen Bestandteile. Jede Zeile ist ein Datensatz, jeder Datensatz besteht aus Feldern. Das Zeichen, das die Felder trennt, kannst du über CSVParser.Delimiter festlegen. Zum Schreiben, andererseits, verwendest du den TCSVBuilder, der die Datensätze aus den Feldern zusammensetzt. Aber ehrlich gesagt: Eigentlich sind das nichts als einfache String-Operationen...
BitRausch hat geschrieben:Die Datensätze die eingelesen werden sollen haben eine Größe von 1200 Byte und bestehen aus 72 Feldern.
Das ist fürs Prinzip egal. Vielleicht kannst du eine Datei hochladen (zur Not mit Dummy-Daten, falls du die echten Daten nicht zeigen willst/kannst). Die Datei bitte in ein zip packen, sonst wird sie von der Forumssoftware nicht akzeptiert.
Ich würde dir gerne ein konkretes Beispiel geben, aber ich weiß nicht, wie deine Daten im Programm/in der Datei vorliegen.
Re: Welche Unit/Komponente für CSV Dateien
Sorry - mein Fehler. Habe es etwas ungeschickt formuliert...
Meinte den TCSVParser wie im Beispiel auf der Wiki...
Zum besseren Verständnis.
- Es werden aus Excel CSV Dateien erzeugt.
- Diese will ich mit meinem Program einlesen.
- Es handelt sich um Text, Zahlen- und Datumsfelder mit Semikolon getrennt. Es sind keine Sonderzeichen in den Textfeldern, auch kein CR.
- nach entsprechender Verarbeitung wird eine neue CSV Datei erstellt wobei die Felder mit bestimmten Formaten abgelegt werden. Z.B Text-Felder linksbündig+nur Großbuchstaben. Zahlen-Felder rechtsbündig und mit '0' aufgefüllt. Keine Trennzeichen, d.h die Felder werden hintereinander weggeschrieben und nur CR/LF am Satzende.
Die Verarbeitung besteht aus Validierung (z.B. Pflichtfelder gefüllt?, Abhängigkeiten erfüllt (z.B. Wenn Feld A gesetzt dann muss auch Feld B gesetzt sein)
und Summenbildung von bestimmten Spalten.
Über die GUI werden die Datensätze nicht angezeigt. Der User kann nur die Datei auswählen und den Prozess starten, da das Ausgabeformat sprich Satzaufbau und Feldformate vorgegeben sind.
Womit tue ich mich schwer?
Ich bin etwas verwirrt bei der Auswahl der richtigen Mittel:
- einfaches TStringList oder CSVDocument oder csvreadwrite beim einlesen?
- Ausgabe in Datei wie in Deinem Beispiel?
(Ich denke eine Datenbank-Komponente wäre hier nicht notwendig.)
Wenn ich Spaltensummen bilden möchte tendiere zu der CSVDocument Lösung ... aber hab hier einfach zu wenig Wissen...
Mir würde eine einfache KISS Lösung reichen.
Ich werde jetzt erstmal für alle 3 Ansätze einfache Testprogramme schreiben und schauen...
ich merke auch das es schon verdammt lang her ist mit dem coden...
Den Quellcode für csvreadwrite habe ich gefunden...
Vielen Dank für Deine Hilfe!! Das ist echt super.
Die Daten müsste ich erst anonymisieren...
Meinte den TCSVParser wie im Beispiel auf der Wiki...
Zum besseren Verständnis.
- Es werden aus Excel CSV Dateien erzeugt.
- Diese will ich mit meinem Program einlesen.
- Es handelt sich um Text, Zahlen- und Datumsfelder mit Semikolon getrennt. Es sind keine Sonderzeichen in den Textfeldern, auch kein CR.
- nach entsprechender Verarbeitung wird eine neue CSV Datei erstellt wobei die Felder mit bestimmten Formaten abgelegt werden. Z.B Text-Felder linksbündig+nur Großbuchstaben. Zahlen-Felder rechtsbündig und mit '0' aufgefüllt. Keine Trennzeichen, d.h die Felder werden hintereinander weggeschrieben und nur CR/LF am Satzende.
Die Verarbeitung besteht aus Validierung (z.B. Pflichtfelder gefüllt?, Abhängigkeiten erfüllt (z.B. Wenn Feld A gesetzt dann muss auch Feld B gesetzt sein)
und Summenbildung von bestimmten Spalten.
Über die GUI werden die Datensätze nicht angezeigt. Der User kann nur die Datei auswählen und den Prozess starten, da das Ausgabeformat sprich Satzaufbau und Feldformate vorgegeben sind.
Womit tue ich mich schwer?
Ich bin etwas verwirrt bei der Auswahl der richtigen Mittel:
- einfaches TStringList oder CSVDocument oder csvreadwrite beim einlesen?
- Ausgabe in Datei wie in Deinem Beispiel?
(Ich denke eine Datenbank-Komponente wäre hier nicht notwendig.)
Wenn ich Spaltensummen bilden möchte tendiere zu der CSVDocument Lösung ... aber hab hier einfach zu wenig Wissen...
Mir würde eine einfache KISS Lösung reichen.
Ich werde jetzt erstmal für alle 3 Ansätze einfache Testprogramme schreiben und schauen...
ich merke auch das es schon verdammt lang her ist mit dem coden...
Den Quellcode für csvreadwrite habe ich gefunden...
Vielen Dank für Deine Hilfe!! Das ist echt super.
Die Daten müsste ich erst anonymisieren...
Re: Welche Unit/Komponente für CSV Dateien
Wenn deine Daten ohnehin schon in Excel vorliegen, dann würde ich mal einen Blick auf FPSpreadsheet werfen: http://wiki.lazarus.freepascal.org/FPSpreadsheet Damit wäre der Umweg über CSV nicht mehr notwendig.
knight
knight
Re: Welche Unit/Komponente für CSV Dateien
danke für den Tipp!! FPSpreadsheet kannte ich noch nicht. Ich bekomme nur die CSV Dateien. Das Spreadsheet liegt mir nicht vor.
Re: Welche Unit/Komponente für CSV Dateien
BitRausch hat geschrieben:Womit tue ich mich schwer?
Ich bin etwas verwirrt bei der Auswahl der richtigen Mittel:
- einfaches TStringList oder CSVDocument oder csvreadwrite beim einlesen?
- Ausgabe in Datei wie in Deinem Beispiel?
(Ich denke eine Datenbank-Komponente wäre hier nicht notwendig.)
Wenn ich Spaltensummen bilden möchte tendiere zu der CSVDocument Lösung ... aber hab hier einfach zu wenig Wissen...
Datenbank-Maschinerie erscheint mir auch überzogen.
Leider weiß ich immer noch nicht, wie die Daten aussehen. Ich denke, ich müsste dir ein einfaches Beispiel schreiben, aber ohne konkrete Angaben ist das genauso abstrakt wie alles, was bisher gesagt wurde. Denn eigentlich sind alle Hinweise schon genannt.
- af0815
- Lazarusforum e. V.
- Beiträge: 6213
- Registriert: So 7. Jan 2007, 10:20
- OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
- CPU-Target: 32Bit (64Bit)
- Wohnort: Burgenland
- Kontaktdaten:
Re: Welche Unit/Komponente für CSV Dateien
Leichtgewichtig, könnte man SDF nehmen. Damit ist ein SDFDataSet möglich und dann sind zumindest rudimentäre DB Operationen möglich. Es hängt auch davon ab, wie die Daten strukturiert sind. Excel als Vorprodukt kann da jede strukturierte (DB) Verarbeitung verhindern
Sind die CSV-Daten strukturiert ?
Andreas
Sind die CSV-Daten strukturiert ?
Andreas
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).
Re: Welche Unit/Komponente für CSV Dateien
@wp_xyz
Da gebe ich Dir recht. Schreibe gerade ein einfaches Prog mit Hilfe Deiner Tipps/Hinweise, um es mit ein paar einfachen Testdaten zu verdeutlichen. Vielleicht klappt es ja
@Andreas
Vielen Dank!
Was meinst Du mit strukturiert?
Die (CSV) Felder sind in einer bestimmten Reihenfolge.
SDFDataset schau ich mir gleich mal an...
Werde den Code mal posten dann hätten wir eine besser Gesprächsgrundlage...(hoffe ich schaffe es heute noch)
Ich wühle mich durch viele Informationen...
wp_xyz hat geschrieben:BitRausch hat geschrieben:Womit tue ich mich schwer?
Leider weiß ich immer noch nicht, wie die Daten aussehen. Ich denke, ich müsste dir ein einfaches Beispiel schreiben, aber ohne konkrete Angaben ist das genauso abstrakt wie alles, was bisher gesagt wurde. Denn eigentlich sind alle Hinweise schon genannt.
Da gebe ich Dir recht. Schreibe gerade ein einfaches Prog mit Hilfe Deiner Tipps/Hinweise, um es mit ein paar einfachen Testdaten zu verdeutlichen. Vielleicht klappt es ja
@Andreas
Vielen Dank!
Was meinst Du mit strukturiert?
Die (CSV) Felder sind in einer bestimmten Reihenfolge.
SDFDataset schau ich mir gleich mal an...
Werde den Code mal posten dann hätten wir eine besser Gesprächsgrundlage...(hoffe ich schaffe es heute noch)
Ich wühle mich durch viele Informationen...
Re: Welche Unit/Komponente für CSV Dateien
habe jetzt mal ein kleines Prog geschrieben und TCSVDocument verwendet.
War schwieriger als ich dachte - liegt doch alles sehr weit zurück...
Das Prog ist sehr einfach gehalten aber ich hoffe es hilft beim Verständnis...
eine kleine CSV Datei ist mit typischen Feldern, die verarbeitet werden müssen, dabei...
Im Memo wird der Satz so aufgebaut wie er später wieder in eine Datei geschrieben werden sollte... bin aber nicht soweit gekommen um eine entsprechende Routine einzubauen...
Auch die Routine für das bilden der Spaltensummen ist nicht enthalten ... werde mich morgen mal daran setzen...
Womit ich nicht zurechtgekommen bin war der try...final..end Part...und wie die Objekte wieder freigegeben werden ...
Ich bin mir nicht sicher ob TCSVDocument hier das richtige Mittel ist. Wenn ich die Menge der Datensätze denke und die Verarbeitung nach Spalten...
Wie auch immer ... ich würde mich sehr freuen wenn Ihr Euch das mal anschaut und mir euer Feedback geben könntet.
Spart bitte nicht an Kritik und Verbesserungsvorschläge. Ich lerne.
Echt Vielen Dank im Voraus!
War schwieriger als ich dachte - liegt doch alles sehr weit zurück...
Das Prog ist sehr einfach gehalten aber ich hoffe es hilft beim Verständnis...
eine kleine CSV Datei ist mit typischen Feldern, die verarbeitet werden müssen, dabei...
Im Memo wird der Satz so aufgebaut wie er später wieder in eine Datei geschrieben werden sollte... bin aber nicht soweit gekommen um eine entsprechende Routine einzubauen...
Auch die Routine für das bilden der Spaltensummen ist nicht enthalten ... werde mich morgen mal daran setzen...
Womit ich nicht zurechtgekommen bin war der try...final..end Part...und wie die Objekte wieder freigegeben werden ...
Ich bin mir nicht sicher ob TCSVDocument hier das richtige Mittel ist. Wenn ich die Menge der Datensätze denke und die Verarbeitung nach Spalten...
Wie auch immer ... ich würde mich sehr freuen wenn Ihr Euch das mal anschaut und mir euer Feedback geben könntet.
Spart bitte nicht an Kritik und Verbesserungsvorschläge. Ich lerne.
Echt Vielen Dank im Voraus!
- Dateianhänge
-
- CSVTest2.zip
- Testprog für TCSVDocument
- (128.11 KiB) 117-mal heruntergeladen
Re: Welche Unit/Komponente für CSV Dateien
Ganz grob ist das schon in Ordnung. Nur würde ich beim Einlesen der Datei eher die Strategie verfolgen, die Strings so schnell wie möglich loszuwerden, denn mit Strings kannst du nicht rechnen. Daher würde ich den Record TDatensatz statt mit Strings gleich mit den richtigen Datentypen deklarieren:
Und dementsprechend würde ich die eingelesenen Daten nicht in einem CSVDocument speichern (denn da sind sie ja Strings), sondern z.B. in einem Array (oder einer Liste, TList, o.ä - aber das führt für den Wiedereinstieg wahrscheinlich zu weit). Für das Einlesen selbst reicht dann der CSVParser (oder man macht alles gleich "händisch" mit einer StringList, so schwer ist das nicht).
Arrays sind in FPC dynamisch, d.h. man kann mit SetLength() ihre Länge zur Laufzeit festlegen. Da die Anzahl der Zeilen nicht bekannt ist, reserviere ich immer in Blöcken von, z.B., 1000 Arrayelementen und lasse einen Zähler mitlaufen. Immer wenn der Zählerstand ohne Rest durch 1000 teilbar ist, ist der Block voll, und es wird die Arraylänge um weitere 1000 Elemente vergrößert. Am Ende des Einlesens wird dann das Array auf die nun bekannte Länge gtrimmt. (Man könnte das Array auch nach jeder Zeile um 1 Element verlängern, aber wenn die Datei groß ist, wird man das permanente umkopieren der Daten in der Geschwindigkeit merken).
Beim Umwandeln der Strings in Zahlen gibt es noch die Unschönheit, dass deine Werte einen Tausendertrenner enthalten, was keine der vorhanden Konvertierungsroutinen mag. Daher habe ich eine Routine ThSepStrToFloat geschrieben, die zuerst das Tausendertrennzeichen aus dem String entfernt und dann die Konvertierung durchführt.
Damit man etwas sieht habe ich zum Schluss das Daten-Array in einem Memo ausgegeben. Dabei kommt die Format()-Routine zum Einsatz, mit der man alle möglichen Datentypen zu einem String zusammen bauen kann. Ist am Anfang sicher umgewohnt, aber später unerlässlich, um ellenlange Stringverkettungsoperationen zu vermeiden. Schau dir die Dokumentation dazu an (https://www.freepascal.org/docs-html/rt ... ormat.html).
Deine eigenen Routinen zum Auffüllen eines Strings auf eine bestimmte Länge sind unnötig, sie gibt es schon in der Unit StrUtils (PadLeft und PadRight).
Das Memo musst du beim Programmende selbst nicht freigeben, das macht das Formular automatisch.
try-Böcke verwende ich in der Regel nur zusammen mit "finally". Da dabei der finally-teil immer durchlaufen wird, auch wenn im Block vorher ein Fehler aufgetreten ist, kann so sichergestellt werden, dass angeforderte Resourcen wieder freigegeben werden. In dem Beispiel kommt das mehrmals vor: Zum Beispiel wird der CSVParser erzeugt, und durch den finally-Abschnitt wird sichergestellt, dass der Parser wieder freigegeben wird. Also
P.S.
Das beigefügte Demo-Programm funktioniert soweit. Allerdings habe ich nicht getestet, ob das Verlängern des Arrays richtig funktioniert, wenn der vorab reservierte Block voll ist. Normalerweise ist das kein Problem, aber da die Header-Zeile der Datei ja nicht ins Datenarray eingelesen wird, muss man das im Zähler korrigieren, und da rutscht ein -1 leicht an die falsche Stelle.
Code: Alles auswählen
type
TDatensatz = record
IDNr: Integer;
LfdNr: Integer;
Zahl: Double;
Datum: TDate;
Text: String;
Betrag: Double;
end;
Und dementsprechend würde ich die eingelesenen Daten nicht in einem CSVDocument speichern (denn da sind sie ja Strings), sondern z.B. in einem Array (oder einer Liste, TList, o.ä - aber das führt für den Wiedereinstieg wahrscheinlich zu weit). Für das Einlesen selbst reicht dann der CSVParser (oder man macht alles gleich "händisch" mit einer StringList, so schwer ist das nicht).
Arrays sind in FPC dynamisch, d.h. man kann mit SetLength() ihre Länge zur Laufzeit festlegen. Da die Anzahl der Zeilen nicht bekannt ist, reserviere ich immer in Blöcken von, z.B., 1000 Arrayelementen und lasse einen Zähler mitlaufen. Immer wenn der Zählerstand ohne Rest durch 1000 teilbar ist, ist der Block voll, und es wird die Arraylänge um weitere 1000 Elemente vergrößert. Am Ende des Einlesens wird dann das Array auf die nun bekannte Länge gtrimmt. (Man könnte das Array auch nach jeder Zeile um 1 Element verlängern, aber wenn die Datei groß ist, wird man das permanente umkopieren der Daten in der Geschwindigkeit merken).
Beim Umwandeln der Strings in Zahlen gibt es noch die Unschönheit, dass deine Werte einen Tausendertrenner enthalten, was keine der vorhanden Konvertierungsroutinen mag. Daher habe ich eine Routine ThSepStrToFloat geschrieben, die zuerst das Tausendertrennzeichen aus dem String entfernt und dann die Konvertierung durchführt.
Damit man etwas sieht habe ich zum Schluss das Daten-Array in einem Memo ausgegeben. Dabei kommt die Format()-Routine zum Einsatz, mit der man alle möglichen Datentypen zu einem String zusammen bauen kann. Ist am Anfang sicher umgewohnt, aber später unerlässlich, um ellenlange Stringverkettungsoperationen zu vermeiden. Schau dir die Dokumentation dazu an (https://www.freepascal.org/docs-html/rt ... ormat.html).
Deine eigenen Routinen zum Auffüllen eines Strings auf eine bestimmte Länge sind unnötig, sie gibt es schon in der Unit StrUtils (PadLeft und PadRight).
Das Memo musst du beim Programmende selbst nicht freigeben, das macht das Formular automatisch.
try-Böcke verwende ich in der Regel nur zusammen mit "finally". Da dabei der finally-teil immer durchlaufen wird, auch wenn im Block vorher ein Fehler aufgetreten ist, kann so sichergestellt werden, dass angeforderte Resourcen wieder freigegeben werden. In dem Beispiel kommt das mehrmals vor: Zum Beispiel wird der CSVParser erzeugt, und durch den finally-Abschnitt wird sichergestellt, dass der Parser wieder freigegeben wird. Also
Code: Alles auswählen
csv := TCSVParser.Create;
try
// tue etwas mit dem Parser
finally
// Parser wird nicht mehr benötigt
csv.free;
end;
P.S.
Das beigefügte Demo-Programm funktioniert soweit. Allerdings habe ich nicht getestet, ob das Verlängern des Arrays richtig funktioniert, wenn der vorab reservierte Block voll ist. Normalerweise ist das kein Problem, aber da die Header-Zeile der Datei ja nicht ins Datenarray eingelesen wird, muss man das im Zähler korrigieren, und da rutscht ein -1 leicht an die falsche Stelle.
- Dateianhänge
-
- csv_test-wp.zip
- (3.71 KiB) 144-mal heruntergeladen
Re: Welche Unit/Komponente für CSV Dateien
wow wp_xyz!!
Sehr geil! Vielen Dank!!
Ich muss mir das morgen genauer ansehen und verstehen ... ist jetzt zu spät...
Ich sehe jetzt schon vieles was ich checken kann/muss...Viel Input - sehr gut!
Das war/ist eine super Hilfe - vielen Dank für Deine Zeit wp_xyz!!
Hab mir den ganzen Tag dafür eingeplant...wer braucht schon Sonne
Sehr geil! Vielen Dank!!
Ich muss mir das morgen genauer ansehen und verstehen ... ist jetzt zu spät...
Ich sehe jetzt schon vieles was ich checken kann/muss...Viel Input - sehr gut!
Das war/ist eine super Hilfe - vielen Dank für Deine Zeit wp_xyz!!
Hab mir den ganzen Tag dafür eingeplant...wer braucht schon Sonne
Re: Welche Unit/Komponente für CSV Dateien
@wp_xyz
Dein Beispiel sagt mehr als 1000 Worte!
Seit heute morgen sitze ich daran und folge Deinen Vorschlägen...
Bei der Umgestaltung des Records bin ich auf folgende Frage gestoßen:
- Ich habe numerische Felder mit 15 Vorkomma+2 Nachkomma = 17 Stellen - welchen Datentyp entspricht das - ist double hier richtig?
- dumme frage: Felder mit Prozentzahlen 3 Vorkomma+3 Nachkomma = 6 Stellen - entspricht das dem type single?
Bei Deinem Beispiel
bekomme ich es nicht hin z.B Zahl mit führenden '0' aufzufüllen. Entweder finde ich das passende Formatzeichen nicht oder ich müsst jedes Feld einzeln formatieren
z.B.
s:= AddChar('0',PadLeft('Zahl', 19),19) )+' ' + ...
Oder übersehe ich da etwas?
Dein Beispiel sagt mehr als 1000 Worte!
Seit heute morgen sitze ich daran und folge Deinen Vorschlägen...
Bei der Umgestaltung des Records bin ich auf folgende Frage gestoßen:
- Ich habe numerische Felder mit 15 Vorkomma+2 Nachkomma = 17 Stellen - welchen Datentyp entspricht das - ist double hier richtig?
- dumme frage: Felder mit Prozentzahlen 3 Vorkomma+3 Nachkomma = 6 Stellen - entspricht das dem type single?
Bei Deinem Beispiel
Code: Alles auswählen
memLog.Lines.Clear;
s := PadLeft('ID', 3) + ' ' + PadLeft('LfdNr', 7) + ' ' +PadLeft('Zahl', 19)+ ' ' +
PadLeft('Datum', 15) + ' ' + PadRight('Text', 10) + ' ' + PadLeft('Betrag', 19);
memLog.Lines.Add(s);
for i := 0 to High(Data) do begin
with Data[i] do
s := Format('%3d %7d %19.2f %10s %-20s %19.2f', [
IDNr, LfdNr, Zahl, DateToStr(Datum), Text, Betrag
]);
memLog.Lines.Add(s);
bekomme ich es nicht hin z.B Zahl mit führenden '0' aufzufüllen. Entweder finde ich das passende Formatzeichen nicht oder ich müsst jedes Feld einzeln formatieren
z.B.
s:= AddChar('0',PadLeft('Zahl', 19),19) )+' ' + ...
Oder übersehe ich da etwas?
Re: Welche Unit/Komponente für CSV Dateien
BitRausch hat geschrieben:- Ich habe numerische Felder mit 15 Vorkomma+2 Nachkomma = 17 Stellen - welchen Datentyp entspricht das - ist double hier richtig?
- dumme frage: Felder mit Prozentzahlen 3 Vorkomma+3 Nachkomma = 6 Stellen - entspricht das dem type single?
Immer wenn ein Dezimalpunkt drinnen ist, brauchst du Single, Double oder Extended, in dieser Reihenfolge mit wachsender Genauigkeitsanforderung (https://de.wikibooks.org/wiki/Programmi ... d_Extended); ich nehme routinemäßig meistens Double, als Kompromiss sozusagen. Mit max 3 Nachkommastellen ist Single völlig ausreichend, du wirst schon die Extremwerte von -E45 ... + E38 nicht überschreiten.
Bei diesen Gleitkommazahlen sind Rundungsfehler zu beachten, d.h. du darfst dich nicht darauf verlassen, dass 1.2 + 2.4 = 3.6 ist, denn die nächste binäre Zahl zu 1.2 könnte in Wirklichkeit 1.20000003 und zu 2.4 könnte es 3.3999999999 sein. Also bei Vergleichen immer die Funktion SameValue aus math verwenden, hier kann man einen Genauigkeitsparameter angeben, den ich üblicherweise eher großzügig ansetze (bei Single etwa 1E-6 - d.h. du vergleichst die ersten 6 Nachkommastellen).
Bei Geldbeträgen gibt es noch den Typ Currency, eigentlich ein Integer-Typ, der 4 Nachkommastellen darstellen kann. Hier gibt es keine Rundungsfehler.
Prozentzahlen gibt es in Pascal nicht. Du musst aus dem String das Prozentzeichen entfernen, den Rest in eine Gleitkommazahl umwandlen (Single reicht), und durch 100 dividieren.
BitRausch hat geschrieben:Bei Deinem BeispielCode: Alles auswählen
memLog.Lines.Clear;
s := PadLeft('ID', 3) + ' ' + PadLeft('LfdNr', 7) + ' ' +PadLeft('Zahl', 19)+ ' ' +
PadLeft('Datum', 15) + ' ' + PadRight('Text', 10) + ' ' + PadLeft('Betrag', 19);
memLog.Lines.Add(s);
for i := 0 to High(Data) do begin
with Data[i] do
s := Format('%3d %7d %19.2f %10s %-20s %19.2f', [
IDNr, LfdNr, Zahl, DateToStr(Datum), Text, Betrag
]);
memLog.Lines.Add(s);
bekomme ich es nicht hin z.B Zahl mit führenden '0' aufzufüllen. Entweder finde ich das passende Formatzeichen nicht oder ich müsst jedes Feld einzeln formatieren
z.B.
s:= AddChar('0',PadLeft('Zahl', 19),19) )+' ' + ...
Oder übersehe ich da etwas?
Um eine Integer in der Format-Anweisung mit führenden Nullen darzustellen, musst du einen Punkten vor die Längenangabe setzen, also Format('%.3d', [IDNr]) statt Format('%3d', [IDNr]). Bei Gleitkommazahlen kenne ich das nicht, weil der Punkt da den Dezimalpunkt bezeichnet und die folgende Zahl als Zahl der Dezimalstellen genommen ist. Hier musst du führende Nullen manuall ergänzen:
Code: Alles auswählen
function PadZeroLeft(x: Double; Decimals, MaxLen: Integer): String;
begin
Result := Format('%.*f', [Decimals, x]); // Das Symbol '*' wird durch den Wert von Decimals ersetzt
while Length(Result) < MaxLen do Result := '0' + Result; // o.ä.
end;