.csv Dateien nicht kompatibel (umwandeln)

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
Antworten
siro
Beiträge: 787
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

.csv Dateien nicht kompatibel (umwandeln)

Beitrag von siro »

Hallo zusammen,
ich sitze grade an einem recht trivialen Problem, doch zuvor die Hintergründe.

Ich habe eine kleine Schaltung mit 3 Tastern. Diese Taster habe ich per Software entprellt.
Meiner Meinung nach auch recht "ordentlich"
Doch es traten immer wieder mehrfach Auslösungen auf (Prellen).
Einmal drauf tippen, Wert hat sich 2 oder 3 mal verändert.

Nachdem ich mir das Signal mit dem Oszilloskop angesehen habe, traute ich meinen Augen nicht :shock:
Einen derart schlechten Taster habe ich noch nie erlebt.
Wie dem auch sei, um meine Software auch für solche Exoten anzupassen, wollte ich diese Prellkurven
mit dem Rigol Ossi MSO1104 aufzeichen und in meinen Funktionsgenerator Siglent SDG2122X übertragen.
So kann ich sie Manuell auslösen und schauen wie sich meine Software verhält.

Leider sind die .csv Formate aber nicht annähernd kompatibel.
Mal abgesehen vom Header, der lässt sich mit einem Texteditor recht schnell ändern,
hat das Format vom Oszilloskop hinter dem Messwert, vor dem CR LF noch ein Komma.
Keine Ahnung warum. Das gehört da nicht hin....
also:

Code: Alles auswählen

0,4.88e+00,<CR><LF>
1,4.88e+00,<CR><LF>
2,5.04e+00,<CR><LF>
3,4.88e+00,<CR><LF>
4,5.04e+00,<CR><LF>
5,5.04e+00,<CR><LF>
6,5.04e+00,<CR><LF>
normal wäre:

Code: Alles auswählen

0,4.88e+00<CR><LF>
1,4.88e+00<CR><LF>
2,5.04e+00<CR><LF>
3,4.88e+00<CR><LF>
4,5.04e+00<CR><LF>
5,5.04e+00<CR><LF>
6,5.04e+00<CR><LF>
Ich muss also das Komma vor dem Carriage Return Linefeed entfernen.
Das könnte man auch im Texteditor machen, aber nicht mehr mit tausenden/hunderttausenden von Zeilen...

So habe ich das mit dem Texteditor Notepad++ versucht.
Nach einigen Versuchen klappte das auch, aber zwischenzeitlich kann ich Mittag essen gehen.
Er hat für 1 Million Daten tatsächlich knapp 17 Minuten (nicht Sekunden !!) gebraucht um mir jeweils dieses olle Komma zu entfernen.

Zum Glück gibt es ja Pascal (Lazarus) so habe ich mir überlegt wie ich das wohl machen könnte
und daraus entstand ein Code, der für 1 Millionen Zeilen grade mal 87 Millisekunden benötigt.

Übrigens : dieses falsche/ungewöhnliche csv Format kann man nicht in ein TValueListEditor laden.
Die Komponente denkt dass noch ein dritter Wert kommt und meckert...wegen dem überflüssigen Komma.
In ein TStringGrid geht das, da werden dann 3 Spalten erstellt, obwohl ja eigentlich nur zweie existieren.

Doch nun zum wirklich einfachen Code, ohne große Sicherheiten oder Fehler abzufangen:

Code: Alles auswählen

var freq,tStart,tStop:Int64;
var t:Single;   

procedure ConvertCsv(const FileName:string);
var f:File;
    FSize,cnt,i:Int64;
    Buffer:^Byte;
    a,b:^Byte;
begin
  AssignFile(f,Filename);
  Reset(F,1);                 // 1 = Blockgröße ist 1 Byte
  FSize:=FileSize(f);         // Grösse der Datei in Bytes ermitteln
  GetMem(Buffer,FSize);       // Speicher reservieren
  BlockRead(f,Buffer^,FSize); // Datei komplett einlesen

  // Vorbereitung zum Zeit messen:
  QueryPerformanceFrequency(freq);
  QueryPerformanceCounter(tStart);

  // hier beginnt die eigentliche Konvertierung:
  cnt:=0;                         // Zähler für gelöschte Komma(ta)
  a:=Buffer;                      // ersten  Zeiger auf den Datenspeicher
  b:=Buffer;                      // zweiten Zeiger auf den Datenspeicher
  for i:=0 to FSize-1 do begin    // all Bytes durchlaufen
    a^:=b^;                       // kopiere Zeichen vom Zeiger b zum Zeiger a (ja, ich weis die sind erstmal gleich)
    inc(a);                       // Zeiger auf nächstes Zeichen
    inc(b);                       // Zeiger auf nächstes Zeichen
    if b^=$0D then begin  // steht Zeiger b auf einem CR (carriage Return)
      dec(a);             // erniedrigen wir den Zeiger a wieder, weil der steht dann auf dem BÖSEN Komma
                          // dadurch wird das Komma beim nächsten Durchgang überschrieben..
                          // die Daten rutschen dadurch immer wieder ran.
      inc(cnt);           // cnt brauchen wir später für die neue Dateigröße
    end;
  end;

  QueryPerformanceCounter(tStop);   // Konvertierung fertig, Zeit lesen

  // Konvertierungszeit in Sekunden umrechnen:
  t:=(tStop-tStart) / freq;      // in Sekunden umrechnen
  Form1.caption:=FloatToStr(t);  // und ausgeben

  CloseFile(f);  // Die originale Datei schliessen

  // Neue geänderte Datei schreiben:
  AssignFile(f,'Test.csv');
  Rewrite(f,1);
  Blockwrite(f,Buffer^,FSize-cnt);   // diese wird entsprechend kleiner
  CloseFile(f);

  FreeMem(Buffer,FSize);    // achja, den Speicher wieder freigeben...
end;                
ich wollte damit nur mal zeigen, wie effektiv man mit Pascal Probleme bewältigen kann.
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

shokwave
Beiträge: 490
Registriert: Do 15. Nov 2007, 16:58
OS, Lazarus, FPC: Win11/Ubuntu Budgie/macOS (Laz 4.x FPC 3.2.x)
CPU-Target: i386, x64, arm64
Wohnort: Gera

Re: .csv Dateien nicht kompatibel (umwandeln)

Beitrag von shokwave »

OK, das ist nett. Ich frage mich nur, warum du dich mit dem LowLevelKram rumärgerst.

Code: Alles auswählen

procedure ConvertCsv(const AFilename: string);
var
  Lines: TStringList;
  i: integer;
begin
  Lines := TStringList.Create;
  Lines.LoadFromFile(AFilename);

  for i := 0 to Lines.Count - 1 do
  begin
    Lines[i] := Copy(Lines[i], 1, Length(Lines[i]) - 1);
  end;

  Lines.SaveToFile('test2.csv');
  FreeAndNil(Lines);
end;

Das ist meiner Meinung nach leserlicher und auch nicht wirklich langsamer. 0,14s auf einem X1-26-100 für die gesamte Funktion, also inkl. lesen und schreiben der Datei.
mfg Ingo

siro
Beiträge: 787
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: .csv Dateien nicht kompatibel (umwandeln)

Beitrag von siro »

Hallo shokwave,

ersteinmal Danke, dass Du dich diesem Problem mal gewidmet hast.

Ich war erstmal erstaunt, ich hätte nicht annähernd gedacht, dass man derartig gut "lesbaren" Code dafür schreiben kann.
Ganz ehrlich, das gefällt mir natürlich wesentlich besser als meine Pointerei :roll:
und das funktioniert auch wirklich erstaunlich schnell.

Habe das auf meinem Rechner grade nochmal getestet mit Lesen und Schreiben
1 Million Datensätze mit Lesen und Schreiben bei Dir 2,53 Sekunden
Bei mir mit Lesen und Schreiben 0,012 Sekunden.
Mein Code ist zwar noch um den Faktor 210 schneller, aber eigentlich völlig irrelevant.

Ich glaube ich übernehme deinen Code. :wink:

Vielen Dank für deine Mühe.
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

Benutzeravatar
theo
Beiträge: 11377
Registriert: Mo 11. Sep 2006, 19:01

Re: .csv Dateien nicht kompatibel (umwandeln)

Beitrag von theo »

Ich würde es auch wie shokwave machen, vllt. noch so

Code: Alles auswählen

uses StrUtils;  
..
  Lines.BeginUpdate;
  for i := 0 to Lines.Count - 1 do
    Lines[i] := TrimRightSet(Lines[i],[',']);
  Lines.EndUpdate;        
...
Siro, dein Code hat leider kaum Vorteile.
Mit "TextFile" statt "File" könntest du wenigstens zeilenweise (Readln, Writeln) arbeiten. Eine Textdatei ist es ja.
Das wäre schon viel einfacher und dann könnte man argumentieren, dass nicht die ganze Datei in den Speicher gelesen werden muss.
So ist es einfach maximal kompliziert. :wink:
Ja, schnell in der Ausführung ist der Code wahrscheinlich.

https://wiki.freepascal.org/File_Handli ... procedures

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

Re: .csv Dateien nicht kompatibel (umwandeln)

Beitrag von wp_xyz »

So schlecht finde ich siro's Methode auch wieder nicht. Vor allem: sie ist schnell. Und mit den Kommentaren auch gut zu verstehen. Nur der Geschwindigkeitsvergleich ist unfair. Bei siro wird nur die Zeit für die Prozessierung des Zeilenendes gemessen, bei TStringList ist ein wesentlicher Teil, das Lesen der Datei, in der Messung mit enthalten. Ich habe daher ein "faires" Testprogramm geschrieben, in dem die drei Verfahren - siro, stringlist, textfile - inklusive Lesen und Schreiben verglichen werden - siehe Anhang. ("create_datafile" erzeugt eine Test-Datei mit 1 Millionen Einträgen, "test_project" führt den Test aus).

Ergebnis:

Code: Alles auswählen

Siro: 234ms
TextFile: 313ms
StringList: 6328ms
Ich finde, die TextFile-Methode ist die beste: sehr schnell, fast so schnell wie siro's, einfach zu verstehen, und hat nur 2*BUF_SIZE Bytes (10000) im Speicher (die SetTextBuf-Aufrufe kann man auch weglassen, dann wird sie etwa einen Faktor 2-3 langsamer).
Dateianhänge
CSV_TrimComma.zip
(2.51 MiB) 9-mal heruntergeladen

Benutzeravatar
theo
Beiträge: 11377
Registriert: Mo 11. Sep 2006, 19:01

Re: .csv Dateien nicht kompatibel (umwandeln)

Beitrag von theo »

wp_xyz hat geschrieben: Di 28. Jul 2026, 10:45 So schlecht finde ich siro's Methode auch wieder nicht. Vor allem: sie ist schnell.
Nein, nicht grundsätzlich schlecht, das hat niemand gesagt.
Ich würde nur für so ein quick & dirty Einweg-Tool, was siro ja anscheinend will, nicht so viel Hirnschmalz einsetzen wollen.
Damit wächst auch die Fehleranfälligkeit.
So reicht doch und das kann man im Halbschlaf programmieren:

Code: Alles auswählen

procedure ConvertCsv(const AFilename: string);
var
  Lines: TStringList;
  i: integer;
begin
  Lines := TStringList.Create;
  Lines.LoadFromFile(AFilename);
  Lines.BeginUpdate;
  for i := 0 to Lines.Count - 1 do
    Lines[i] := TrimRightSet(Lines[i],[',']);
  Lines.EndUpdate;
  Lines.SaveToFile('test2.csv');
  Lines.Free;    
end;     

siro
Beiträge: 787
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: .csv Dateien nicht kompatibel (umwandeln)

Beitrag von siro »

Insgesamt lässt sich sagen. Viele Wege führen zum Ziel.
Jede Methode hat ihre daseins Berechtigung. Auch die von wp_xyz, da diese am wenigsten Speicher benötigt.
Auch Theos Methode probiert. Funktioniert auch super.
Meine ist mit Abstand die "komplizierteste" das muss ich zugeben. Ich bin halt noch so "Old School" :mrgreen:

Hab grad mal den Code von wp_xyz auf 3 verschiedenen Rechnern laufen lassen:
Jeweils 2 Durchläufe:
-------------------------------------------
Intel Celeron 1,35 GHz

Siro: 360ms
TextFile: 3265ms
StringList: 23563 ms

Siro: 250ms
TextFile: 3329ms
StringList: 23359ms
------------------------
HP Laptop Intel I5 3,1 GHz

Siro: 172ms
TextFile: 687ms
StringList: 4110ms

Siro: 110ms
TextFile: 687ms
StringList: 4125ms
------------------------------------
Medion Laptop Intel I3 2,1 GHz

Siro: 203ms
TextFile: 2328ms
StringList: 12266ms

Siro: 203ms
TextFile: 2391ms
StringList: 12109ms
----------------------

Habt Dank für eure Anteilnahme und euren Code. Da kann ich mir wirklich noch was von abgucken.
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

pascoli
Beiträge: 19
Registriert: So 4. Mai 2025, 21:21

Re: .csv Dateien nicht kompatibel (umwandeln)

Beitrag von pascoli »

Nun ja, ich würde sed nehmen -:)

$ time sed s/,.$// testdata.txt > testdata-clean.txt

real 0m0,309s
user 0m0,267s
sys 0m0,040s

(Intel i5-6600 3.3 GHz)

Antworten