![]() |
CSV-Reader. Schnelles lesen von CSV-Dateien
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,
Ich habe mal eine Klasse geschrieben, die CSV-Dateien einliest und die einzelnen Elemente extrahiert. Dabei werden auch in '"' eingeschlossenen Strings korrekt erkannt. Verwendet wird es so:
Delphi-Quellcode:
Es ist ziemlich flott. Vielleicht kann Jemand etwas damit anfangen.
Var
csvReader : TCSVReader; sData : TFileStream; Begin sData := TFileStream.Create('Sample.CSV',fmOpenRead); csvReader := TCSVReader.Create (sData, ';'); csvReader.First; // Nach Änderung auf Veranlassung von Grenzgaenger nun notwendig. Dank an 'deadcandance' Try While not csvReader.Eof Do Begin For i:=0 to csvReader.ColumnCount - 1 Do Memo.Lines.Add (csvReader.Columns[i]); csvReader.Next; End; Finally csvReader.Free; sData.Free; End; End; Bugreports und Verbesserungsvorschläge sind natürlich erwünscht. Bug: Grenzgaenger hat mich auf einen Fehler in der Behandlung von '""' aufmerksam gemacht. Weiterhin wünschte er sich den Quote-Character als Eigenschaft. Bitte sehr. Erweiterung: ralfschwalbe hätte gern, das die Klasse auch mit UNIX-Umbrüchen umgehen kann. Bitte sehr. Erweiterung: Noch besser: Man kann nun das EOL-Zeichen und die Länge selbst angeben (z.B. für Unix #10 und 1, Windows: #13 und 2) Änderung: Laut grenzgaenger hat der Aufruf von 'First' im Konstruktor nix zu suchen. Recht hat er! Bug: Kater Karlo hat einen Fehler im Konstruktor entdeckt, der bei Streams auftritt, deren Position>0 ist. Weiterhin ist ein überflüssiges privates Feld entfernt worden. |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Hi,
super Sache! Aber ein Hinweis aus eigener Erfahrung: Du solltest unbedingt noch bedenken, dass es auch csv-Dateien mit UNIX Zeilenumbrüchen gibt! Nicht das irgendwann mal was "schiefgeht"! |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Hi ralf,
UNIX-Zeilenumbrüche sind nur ein CR, richtig? Das sollte kein Problem sein. Neue Version oben |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Hi alzaimar,
nimm LF - ist besser so. Freundliche Grüße |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Hallo Marabu. Na gut, dann muss ich ja nochmal nachdenken :freak:
|
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
hallo alzheimer,
vielleicht solltest du von den optionen her MAC, Unix und DOS unterstützen ... die trennzeichen sind wie folgt #13, #10, #13#10... und für kümimüsis #10#13 ;-), welche es eben verwechseln ... :-) grüsse GG |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Ein CSV Feldtrennzeichen kann ',' oder ';' sein.
Verarbeitest du beides oder nur eins von beiden? U.u. wäre es dann gut als property das Trennzeichen mit angeben zu können. |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Schau Dir mal den constructor an:
Delphi-Quellcode:
Constructor TCSVReader.Create(aStream: TStream; aDelimiter: Char);
Zitat:
|
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Zitat:
|
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Naja, vielleicht machen das manche Programme so mit dem ListSeparator. Aber die ganze Geschichte steht
![]() |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Zitat:
|
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Jedenfalls ist Deine Lösung gut und praktikabel. Eigentlich geht es ja nicht um die Therorie, sondern dass man es für bestehende "CSV"-Definitionen verwenden kann. Ich bin auch erst vor kurzem darauf gekommen, als ich zum Debuggen eine "CSV" mit Semikolon ausgegeben habe und der Microsoft LogParser das Format nicht erkannte. Die nennen das dann TSV mit Delimiter =";".
|
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Zitat:
Zitat:
|
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Und wenn Du noch
Delphi-Quellcode:
ersetzt mit
SetString(Result, spFirst, spLen);
Delphi-Quellcode:
dann wird das auch schnell;)
SetLength(result, spLen);
Move(spFirst^, result[1], spLen); |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Kleiner Hinweis. Das o.g. Bespiel funktioniert so nicht ganz.
Beim ersten Durchlauf ist ColumnCount = 0, erst nach dem Aufruf von csvReader.Next wird ColumnCount gesetzt. Sollte doch dann eigentlich so sein:
Delphi-Quellcode:
Ansonsten, :thumb:
Try
if not csvReader.Eof then csvReader.Next; While not csvReader.Eof Do Begin For i:=0 to csvReader.ColumnCount - 1 Do Memo.Lines.Add (csvReader.Columns[i]); csvReader.Next; End; Finally csvReader.Free; sData.Free; End; Viele Grüße, deadcantdance |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
In der kleinen Demo fehlt der Aufruf von 'csvReader.First'. Habe ich nachgetragen (siehe Eingangspost).
|
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
:coder2:
Hi, ggf. ist es sinnvoll im Constructor vor dem Read die Position des zu lesenden Stream auf Null zu setzen. Es sei denn es ist beabsichtigt erst ab der aktuellen Stelle einzulesen. Die Variable fReader: TReader; kann weggelassen werden. Gruss Kater Karlo |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Hallo Kater Karlo,
Es ist üblich, das der Aufrufer den Stream an die geeignete Stelle setzt, das muss er bei Verwendung anderer Komponenten (z.B. Crypter/Packer-Streams) auch selbst machen. Bei Instantiierung eines TFileStreams ist die Position eh bei 0. Bezüglich deines Einwands mit dem Buffer fällt auf, das im Konstruktor ein kleine Ungenauigkeit auftritt, wenn die Position des Streams > 0 ist (wie du richtig bemerkt hast). Das sollte nun behoben sein. Danke für die Anregungen |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Hallo alzaimar,
ich hätte da noch einige Anregungen: - Als Eigenschaft den Index der aktuellen Zeile - Als Eigenschaft die aktuelle Zeile als String - Veröffentlichen der Column-Information TStringPos Extended: - Erkennen von Kommentarzeilen (Zeile beginnt mit # oder über eine Eigenschaft bekannt zugebene Zeichenfolge) MfG Kater Karlo |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Kann mir einer vielleicht sagen wie ich beide die Datei zum laufen kriege?
Wenn ich sie abspeichern will sagt er mir immer Declaration of class TForm1 is missing or incorrect. Und bei der zweiten Datei hinterlegt er das
Delphi-Quellcode:
rot wenn ich es Compilieren will. kann mir wer helfen?csvReader : TCSVReader; sData : TFileStream; |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Du müsstest die Unit noch einbinden ...
|
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
also ich speicher die Datei csCSV.pas ab und dann erstelle ich ne neue Unit? und unter welchen Namen speicher ich die dann ab bzw. was muss ich daran noc ändern?
|
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Dir scheinen die Grundlagen der Programmierung mit Delphi/Pascal/ObjectPascal zu fehlen. Vielleicht wäre ein
![]() Du kannst Dir auch die Beispiele anschauen, die bei Delphi dabei sind. Dort werden auch Units verwendet ("verwenden" englisch: 'to use' :zwinker: ) |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Woran könnte es liegen, wenn ich statt des Inhalts der CSV nur rechteckige Kästchen einlesen kann?
Sowas (mit den Editor geöffnet) Zitat:
Zitat:
Das CSV wurde mit einem Webinterface erstellt und gespeicht, leider bekomme ich auch nur Kästchen, wenn ich mit Notepad eine test.csv erstelle und selbst mit Daten fülle. |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Zitat:
|
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Heist das, der normale Windowseditor speichert unter Windows XP im Unicodeformat, mit dem ein Memo nix anfangen kann?
|
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Zitat:
|
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Sehr cool! Ich muss viel mit CSV Dateien arbeiten (wobei wir die inzwischen als Char-Separated-Values betiteln ;-) Ich hab inzwischen viele Routinen geschrieben, die immer leichte Varianten und Abweichungen prozessieren.
Was du vielleicht noch überarbeiten soltest wären: - Property ab welcher Zeile die eigentlichen CSV Daten anfangen (häufig gibt es einen Header, der komplett anders aufgebaut ist und davor steht). - eine Comment Property, die alle Zeilen enthält, die nicht als Daten eingelesen werden sollen (z.B. die Zeilen des Headers). - Property für die Anzahl der Elemente pro Zeile (manchmal verrutschen Werte, wenn Messwerte nicht vorliegen, was zu Problemen führt, wenn man alles in eine Tabelle oder für weitere Berechnungen in eine Matrix schreibt). - Für manche Anwendungen ist es interessant, bei der internen Verarbeitung die Delimiter am Anfang einer Zeile zu löschen. - Für andere Anwendungen ist es interessant, mehrere aufeinanderfolgende Delimiter wie einen einzigen zu interpretieren. - eine Möglichkeit eine Zeile anzugeben, welche Spaltenüberschriften trägt (diese sind häufig anders zu prozessieren als die darauffolgenden Daten); quasi die Column Headers. - evtl. tatsächlich eine Option zur Unicode-Behandlung. Bin ja echt gerade am Überlegen, ob ich meine Routinen nicht endlich mal auf eine schicke Klasse umschreiben sollte (man bastelt ja immer so seine Provisorien und die halten dann ewig). Dein Ansatz würde sich mit einigen Anpassungen sehr schön eignen! Liebe Grüße aus ><)))°> Town Jan |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Hallo Jan,
Ich halte deine Vorschläge für nicht unbedingt sinnvoll, denn sie lassen sich bereits durch die vorhandenen Eigenschaften implementieren bzw. gehören imho nicht in die Klasse: Du kannst zum Überspringen der ersten Zeilen die 'Next' Methode ein paar mal aufrufen. Weiterhin gibt dir die Eigenschaft 'ColumnCount' für jede Zeile die Anzahl der Zellen/Spalten zurück. Die Prozessierung bzw. Behandlung des Inhaltes (=Interpretation) obliegt nicht der CSV-Klasse, sondern der benutzenden Anwendung. Die Darstellung als Matrix bzw. Dataset müsste diese Funktionalitäten bereitstellen: Hier geht es nur um das schnelle und korrekte Einlesen von Daten im CSV-Format. Einzig die Unicode-Behandlung wäre eine Option. Wobei es dann sinnvoller wäre, eine neue Klasse dafür zu implementieren. So wie ich das sehe, müssten die String/Var/PChar-Variablendeklarationen durch ihre Unicode-Pendants ersetzt und an der Stelle 'GetMem' die Anzahl mit 2 multipliziert werden. |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Ich habe mir deinen Code mal etwas genauer angesehen. Mit einigen Punkten hast du recht. Das lag wohl an meiner ersten, eher oberflächlichen, Inspektion :oops: Die Interpretation war natürlich nicht Teil meines Vorschlags. Das habe ich wohl etwas Missverständlich ausgedrückt. Ist klar, daß sich jede Anwendung die Daten dann in ihr eigenes Datenhandlingkonstrukt stopfen muss.
Bezüglich der 'Lösche Delimiter am Anfang' und 'Mehrere aufeinanderfolgende als einen Delimiter interpretieren' ermöglicht es gleichzeitig Datenfiles mit fester Spaltenbreite zu importieren. Hier werden ja die ungenutzten Zeichen meist mit Leerzeichen aufgefüllt. Durch die angesprochenen Properties könnte man die dann mit verarbeiten (Deli:=SPACE; LöscheAnfangsDelis:=True; MuliDeliAsOne:=True). Ist aber nur ein Vorschlag; letztlich hast du natürlich recht, daß dies nicht CSV Dateien im eigentlichen Sinn sind. Es wäre aber eine Option diese gleich mit zu 'erschlagen' und sicherlich eine Aufwertung deines Codes. Ist ja aber nur ein Vorschlag; du hast ja danach gefragt ;-) Liebe Grüße Jan |
Re: CSV-Reader. Schnelles lesen von CSV-Dateien
Hallo Jan,
Das von Dir beschriebene Datenformat (konstante Spaltenbreite) sind nicht nur keine CSV-Datei "im eigentlichen Sinne", sondern überhaupt nicht. Um Daten mit konstanter Spaltenbreite zu importieren, sollte sich ein anderer Reader implementieren lassen, der dies wesentlich eleganter und vor allem performanter lösen kann: Er benötigt ja nur die Spaltenbreite, den linken Abstand sowie optional ein Füllzeichen. So ein Reader funktioniert intern völlig anders als ein CSV-Reader, wieso sollte man also einen CSV-Reader dafür 'aufwerten' (ich würde das eher 'verwässern' nennen)? Eine Klasse sollte genau einem Zweck dienen (hier: Lesen von CSV-Dateien). Zudem sollte man Klassen auch nicht mit Eigenschaften ausstatten, deren Funktionalität sich mit wenigen Aufrufen der Elementaroperationen selbst nachbilden lässt: Nur leere Zellen am Anfang der Zeile ignorieren:
Delphi-Quellcode:
Leere Zellen ignorieren:
Var
bAtBeginningOfLine : Boolean; iRealColumn : Integer; sCellData : String; ... csvReader.First; While not csvReader.Eof Do Begin bAtBeginningOfLine := True; iRealColumn := 0; For i := 0 to csvReader.ColumnCount - 1 Do Begin sCellData := csvReader.Columns[i]; If bAtBeginningOfLine And (sCellData = '') Then Continue; bAtBeginningOfLine := False; inc (iRealColumn); // Die 'Spaltennummer' ohne leere Zellen am Anfang Memo.Lines.Add (sCellData); End; csvReader.Next; End;
Delphi-Quellcode:
Die OOP-konforme Vorgehensweise hier (für Dich) wäre so:
Var
iRealColumn : Integer; sCellData : String; ... csvReader.First; While not csvReader.Eof Do Begin iRealColumn := 0; For i := 0 to csvReader.ColumnCount - 1 Do Begin sCellData := csvReader.Columns[i]; If sCellData = '' Then Continue; inc (iRealColumn); // Die 'Spaltennummer' Memo.Lines.Add (sCellData); End; csvReader.Next; End; 1. Definieren einer abstrakten Basisklasse ("TableReader"), die die elementaren Operationen zum Lesen von Daten aus Dateien bereitstellt (First, ColumnCount, Next, Eof, Columns). 2. Umschreiben der Klasse CSV-Reader als Nachfolger des "TableReader". 3. Implementieren der Klasse "Constant-Column Reader" als Nachfolger des "TableReader". Du kannst nun auch weitere 'TableReader' implementieren, z.B. die eine SYLK-Datei einlesen, oder direkt mit BIFF arbeiten, oder XML, JSON etc. Der Vorteil ist, das Du diverse schlanke, wartbare und performante Klassen hast, die ohne Probleme erweiterbar und optimierbar sind. Bei einer eierlegenden Wollmilchsauklasse ("God Class") ist dies nicht der Fall. Gruß zurück. |
AW: CSV-Reader. Schnelles lesen von CSV-Dateien
Hallo!
Vielen Dank für die Unit!!! Hat bei mir mit WIN 7 /Lazarus 0.9.30/ FPC 2.4.2 geklappt. Gruß! Andreas |
AW: CSV-Reader. Schnelles lesen von CSV-Dateien
Hi,
funktioniert super! Danke! Selbst große Datenmenge packt der Reader in kürzester Zeit! LG |
AW: CSV-Reader. Schnelles lesen von CSV-Dateien
Hallo,
ich stehe gerade auf dem Schlauch. Die CSV-Datei ist ANSI-kodiert und wird unter XE2 als asiatischer Schriftzeichensalat gelesen. Hab die Datei schon mit TEncoding nach Unicode, UTF8 und UTF16 konvertiert, bringt mich aber alles nicht weiter. Kann mich mal bitte jemand in die richtige Richtung schubsen? |
AW: CSV-Reader. Schnelles lesen von CSV-Dateien
Ich hab die Sourcen gerade nicht zur Hand, aber das hört sich an als ob eine Unterscheidung String/Ansistring fällig wäre.
Gruß K-H |
AW: CSV-Reader. Schnelles lesen von CSV-Dateien
Sorry das ich das alte Thema nochmal ausgrabe, aber ich habe das selbe Problem mit den Unicodeformat und hätte gern gewusst, ob mittlerweile eine Lösung existiert...
|
AW: CSV-Reader. Schnelles lesen von CSV-Dateien
Na ja. Der Code geht davon aus, das ein Char genau ein Byte lang ist. Das muss man nur umschreiben/anpassen.
|
AW: CSV-Reader. Schnelles lesen von CSV-Dateien
OK, aber genau da steh ich gerade etwas auf dem Schlauch... Hat da jemand vielleicht ein Beispiel oder ähnliches für mich?
|
AW: CSV-Reader. Schnelles lesen von CSV-Dateien
Eigentlich geht der Code von PChar als einzelnem Zeichen aus. Unter den Delphis ab...ähm...2009? ist das nunmal PWideChar und nicht PAnsiChar. Eventuell sollte die "Bytigkeit" von Zeichen als weiterer Parameter hinzugefügt werden (was den Code nicht wirklich verschlanken würde). Wenn man aber seinen Anwendungsfall genau kennt, dürfte ein explizites Ersetzen sämtlicher PChar durch PAnsiChar die ursprüngliche ANSI-Funktionalität gewährleisten.
Sherlock (für den dieses "Problem" nur wieder zeigt wie unsinnig es ist einem "Standard"-Typen von Version zu Version andere Basistypen zu verpassen) |
AW: CSV-Reader. Schnelles lesen von CSV-Dateien
Grob gesehen, fast alle '1' mit 'OneChar' und alle '2' mit 'TwoChar' ersetzen, wobei die beiden Symbole entweder Konstanten sind, die über die Compilerversion mit 1/2 oder 2/4 vorbelegt werden, oder eben als Properties.
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:32 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz