[Gelöst] Welche Unit/Komponente für CSV Dateien

Für Fragen von Einsteigern und Programmieranfängern...

Re: Welche Unit/Komponente für CSV Dateien

Beitragvon BitRausch » 1. Jun 2017, 12:14 Re: Welche Unit/Komponente für CSV Dateien

Ok - Formatierung habe ich jetzt so gelöst
Code: Alles auswählen
 
const
  zero = '0';
  space = ' ';
...
 
    s := PadLeft('ID', 3) + ' ' + PadLeft('LfdNr', 7) + ' ' +PadLeft('Zahl', 19)+ ' ' +
         PadLeft('Datum', 10) + '   ' + PadRight('Text', 20) + ' ' + PadLeft('Betrag', 19);
    memLog.Lines.Add(s);
    for i := 0 to High(Data) do begin
      with Data[i] do
          s := AddChar(zero,IntToStr(IDNr),3) + space +
               AddChar(zero,IntToStr(LfdNr),7) + space +
               AddChar(zero,FloatToStr(Zahl),19) + space +
               AddChar(zero,DateToStr(Datum),10) + space +
               PadRight(Text, 20)+ space +
               AddChar(zero,FloatToStr(Betrag),19);         
 
 

Zeroleadingstrings.jpg
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
BitRausch
 
Beiträge: 50
Registriert: 30. Mai 2017, 08:32

Beitragvon BitRausch » 1. Jun 2017, 13:40 Re: Welche Unit/Komponente für CSV Dateien

hab deinen Post erst jetzt gesehen...da war meiner schon raus :wink:

Danke für die Info zu den Zahlentypen!!
Ich hab mir nochmal die Spezifikation der Datensätze angeschaut. Die numerischen Felder mit Nachkommastellen sind fast ausschließlich Währungsfelder (zb. Euro Beträge).
Die Berechnungen sind nur Summenbildungen zb. Summe der Beträge für eine IDNr oder Anzahl der Datensätze einer IDNr.
Ich glaube es macht hier Sinn den Typ Currency zu wählen. Das Feld Prozent wird ohne Sonderzeichen, sprich %, angeliefert . Bsp: *NUR* 3,664.
Meine Lösung zum Format habe ich im anderem Post beschrieben...scheint gut zu funktionieren.

Ich werde heute noch an dem Programm schrauben und auch die Testdaten ausbauen und etwas mehr Masse erzeugen...
Dank Dir habe ich jetzt eine recht gut Vorstellung wie ich die Daten einlesen und zwischenspeichern kann.
Die Daten müssen noch in eine Datei geschrieben werden... Hier komme ich auf Dein Vorschlag bezüglich der StringList zurück. Die scheint mir hier das Mittel der Wahl zu sein da Sie auch bereits 2 Funktionen zum Schreiben mitbringt.

Gerne würde ich eine Log Funktion in das Programm einbauen um Satzfehler und/oder fehlerhafte Felder zu protokollieren.
Ein einfacher Aufbau würde reichen : Datum Zeit Zeilennummer Feld Fehlerbeschreibung
Bsp für eine Fehlerbeschreibung könnte lauten:
- Feld XY ist ein Pflichtfeld und darf nicht leer sein
- Feld XY darf keine Sonderzeichen enthalten

Gibt es unter Lazarus eine Komponente für solche zwecke?
Ich habe im Netz nur Log4d gefunden aber habe nicht den eindruck das es für meine zwecke geeignet wäre...
BitRausch
 
Beiträge: 50
Registriert: 30. Mai 2017, 08:32

Beitragvon wp_xyz » 1. Jun 2017, 14:25 Re: Welche Unit/Komponente für CSV Dateien

BitRausch hat geschrieben:Gerne würde ich eine Log Funktion in das Programm einbauen um Satzfehler und/oder fehlerhafte Felder zu protokollieren.
Ein einfacher Aufbau würde reichen : Datum Zeit Zeilennummer Feld Fehlerbeschreibung
Bsp für eine Fehlerbeschreibung könnte lauten:
- Feld XY ist ein Pflichtfeld und darf nicht leer sein
- Feld XY darf keine Sonderzeichen enthalten

Gibt es unter Lazarus eine Komponente für solche zwecke?

Wahrscheinlich. Aber eine überzüchtete Komponente für sowas, die dir unnötige Abhängigkeiten ins Programm schleppt, ist nicht nötig, das geht mit einer einfachen StringList auch:

Code: Alles auswählen
var
  ErrorLog: TStringList;
 
procedure LogError(AMsg: String);
begin
  ErrorLog.Add(AMsg);
end;
 
// Logfile im OnCreate des Hauptformulare erzeugen (z.B.)...
procedure TForm1.Form1Create(Sender: TObject);
begin
  ErrorLog := TStringList.Create;
end;
 
// ... und im OnDestroy abspeichern und wieder freigeben
procedure TForm1.Form1Destroy(Sender: TObject);
begin
  ErrorLog.SaveToFile(FLogfilename)// Dateinamen irgendwo zu Beginn festlegen
  ErrorLog.Free;
end;
wp_xyz
 
Beiträge: 2249
Registriert: 8. Apr 2011, 08:01

Beitragvon BitRausch » 1. Jun 2017, 14:57 Re: Welche Unit/Komponente für CSV Dateien

sehr cool!!
Habe ich verstanden! Das werde ich so machen.
Hab es mir wahrscheinlich komplizierter vorgestellt als es in Wirklichkeit ist....
BitRausch
 
Beiträge: 50
Registriert: 30. Mai 2017, 08:32

Beitragvon BitRausch » 7. Jun 2017, 12:54 Re: Welche Unit/Komponente für CSV Dateien

so - hab natürlich an dem Code weiter gebastelt.
Das Ergebnis ist im Anhang.

Dank Dir, wp_xyz, und Deinem Beispiel habe ich echt einiges (wieder) gelernt! :D Vielen Dank!!

Für Interessierte:
Zusammengefasst ist in dem Code beispielhaft folgende Funktionalität enthalten:
- Einlesen einer CSV Datei mit TCSVParser
- Ausgabe in einer Textdatei
- Protokollierung/Log
- Stringmanipulations- und Formatierungs- Bespiele
- Ein kleines Regelwerk für Bedingungen auf Feldinhalte
- Summierung von Spalten in einem Array (<--noch Fehlerhaft)

Besonders der letzte Punkt macht mir echt Probleme. Liegt wohl daran das ich schon sehr lange keine Praxis mehr hatte:
Ich bekomme einen "Gruppenwechsel" einfach nicht hin:

Inhalt im Array
Code: Alles auswählen
 
IDNr  ....      Betrag
 
450        88.788,56 
450   2.000.000,18 
650      200.000,00
722   1.900.555,45
733      128.000,65
811      300.000,00
811        45.000,55
922      200.000,00
 


Ergebnis soll so aussehen:
Code: Alles auswählen
 
IDNr            Summe
450           2.088.788,74
650              200.000,00
722           1.900.555,45
733              128.000,65
811              345.000,55
922              200.000,00
 


Code: Alles auswählen
 
//Summe Betrag pro IDNr berechnen
procedure TForm1.SummeBetrag(arData : TDatenArray);
var
  i, j : integer; //Zähler
  sum : Currency;
 
Begin
  sum:= 0;
  for i := 0 to High(arData) do
  begin
      if (arData[i].IDNr <> arData[i+1].IDNr) then
      begin
        memLog.Lines.Add('IDNr ' + IntToStr(arData[i].IDNr) + ' Summe Betrag: ' + FloatToStr(sum)); //Ausgabe
        sum:=0; //Summe zurücksetzen wenn Wechsel
      end
      else
      begin
        sum := sum + arData[i].Betrag;
      end;
    end;
end;                   
 
 


Mein letzter Stand...
Habe schon einiges probiert aber ich habe wohl einen grundsätzlichen Denkfehler...
Annahme ist das die Datensätze sortiert vorliegen.

wp_xyz kannst Du mir vielleicht zeigen wo mein Fehler liegt?
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
BitRausch
 
Beiträge: 50
Registriert: 30. Mai 2017, 08:32

Beitragvon BitRausch » 7. Jun 2017, 14:12 Re: Welche Unit/Komponente für CSV Dateien

ok - ich glaube ich habe es gelöst ...

so scheint es zu funktionieren:

Code: Alles auswählen
 
procedure TForm1.SummeBetrag(arData : TDatenArray);
var
  i : integer; //Zähler
  sum : Currency;
  LastIDNr : integer;
 
Begin
  sum:= 0;
  LastIDNr := arData[0].IDNr;
  for i := 0 to High(arData) do
  begin
      if (LastIDNr <> arData[i].IDNr) then
      begin
        memLog.Lines.Add('IDNr ' + IntToStr(LastIDNr) + ' Summe Betrag: ' + FloatToStr(sum));  //Ausgabe
        sum:=arData[i].Betrag; //Bei Wchsel Sum zurücksetzen
        LastIDNr := arData[i].IDNr;
        if (i = High(arData)) then                                 //letzte Zeile
           memLog.Lines.Add('IDNr ' + IntToStr(LastIDNr) + ' Summe Betrag: ' + FloatToStr(sum));  //Ausgabe
      end
      else
      begin
        sum := sum + arData[i].Betrag;
      end;
  end;
end;             
 
 


nicht elegant ... weiss nicht ob es einen besseren weg gibt...
BitRausch
 
Beiträge: 50
Registriert: 30. Mai 2017, 08:32

Beitragvon wp_xyz » 7. Jun 2017, 14:14 Re: Welche Unit/Komponente für CSV Dateien

Der Fehler ist, dass du nur eine einzige Summe hast, du musst aber eine Summe für jede ID getrennt anlegen. Dazu brauchst du einen Record, bestehend aus ID und Summe, den du wieder in einem Array o.ä. speichert. Du durchläufst alle Datenrecords, bestimmst die ID des aktuellen Records, dann suchst du das Summen-Array nach dieser ID, und wenn du den entsprechenden Eintrag gefunden hast, erhöhst du den aktuellen Summerwert entsprechend - oder wenn es die ID in dem Summen-Array noch nicht gibt, legst du einen neuen Datensatz an.

Ich habe das unten ausformuliert - den Code kannst du einfach irgendwo in den Implementation-Teil der Unit kopieren. Allerdings - sonst wirds ja langweilig - nehme ich diesmal kein Array, denn das ewige Neudimensionieren ist sehr lästig, sondern eine Liste. Eine Liste speichert Pointer, also "irgendwas". Eine spezielle Liste ist TObjectList, die Objekte speichert. Objekte haben einen Destructor, können sich also selbst "zerstören", d.h. du musst dich um die Freigabe der gespeicherten Objekte nicht selbst kümmern - das macht die Liste. Aber welches Objekt soll gespeichert werden? Ich nenne das TSumOfID - die Summe zu einer ID - bestehend aus den Feldern IDNr und Sum. Da wir immer wieder suchen müssen, welches TSumOfID-Objekt eine gewünschte ID hat, habe ich auch TObjectList erweitert zu "TSumList" und eine Methode "FindIndexOfID" geschrieben: Diese Methode durchsucht die Listenenträge nach der betreffenden ID und gibt den Index des Eintrags zurück, bzw. -1, falls diese ID noch nicht vorgekommen ist. Und dann kann man sich auch gleich noch eine Methode schreiben, die einen neuen Datensatz in der Liste einträgt: "AddData". Diese Methode bekommt die ID und den zu addierenden Betrag eines Datensatzes als Parameter. Sie sucht die betreffende ID in TSumList; wenn sie gefunden wird, wird der Wert von Sum um den übergebenen Betrag erhöht; wenn sie noch nicht existiert, wird eine neue Instanz von TSumOfID mit der ID und dem Betrag angelegt und in der Liste gespeichert.

Wenn du also alle Datensätze (im arData) durchläufst, rufst du einfach TSumList.AddData für die aktuelle ID und den zugehörigen Beitrag auf - und alles geht seinen Weg.

Code: Alles auswählen
type
  TSumOfID = class(TObject)
    IDNr: Integer;
    Sum: Currency;
  end;
 
  // Liste, die TSumOfID-Objekte speichert
  TSumList = class(TObjectList)
  public
    function AddData(AIDNr: Integer; AWert: Currency): Integer;
    function FindIndexOfID(AIDNr: Integer): Integer;
  end;
 
// Wertepaar (ID, Wert) hinzufügen: der Wert wird zur bisherigen Summe aller
// Werte derselben ID hinzuaddiert
function TSumList.AddData(AIDNr: Integer; AWert: Currency): Integer;
var
  idx: Integer;
  item: TSumOfID;
begin
  // Angegebene ID in den bisher vorhandenen Listeneinträgen suchen
  idx := FindIndexOfID(AIDNr);
  if idx = -1 then
  begin
    // Nichts gefunden --> neue ID anlegen
    item := TSumOfID.Create;
    item.IDNr := AIDNr;
    item.Sum := AWert;
    Add(item)// Der Liste hinzufügen - das ist von einem Vorfahren geerbt
  end else
  begin
    // Gefunden: AWert dazuaddieren
    item := TSumOfID(Items[idx]);
    item.Sum := item.Sum + AWert;
  end;
end;
 
// Eine ID suchen (nicht optimiert; bei einer sortierten Liste könnte man z.B.
// mit einem optimierten Suchalgorithmus schneller suchen)
function TSumList.FindIndexOfID(AIDNr: Integer): Integer;
begin
  for Result := 0 to Count-1 do
    if TSumOfID(Items[Result]).IDNr = AIDNr then
      exit;
  // Nicht gefunden --> -1 zurückgeben
  Result := -1;
end;
 
//Summe Betrag pro IDNr berechnen
procedure TForm1.SummeBetrag(arData : TDatenArray);
var
  i, id: Integer;
  sumList: TSumList;
  item: TSumOfID;
Begin
  // einen TObjList-Container für alle IDs
  sumList := TSumList.Create;
  try
    // Alle Records durchlaufen...
    for i:=0 to High(arData) do
      // ... und nach ID aufsummieren (das macht idList automatisch)
      sumList.AddData(arData[i].IDNr, arData[i].Betrag);
 
    // Nun alle ID-Einträge durchlaufen...
    for i := 0 to sumList.Count-1 do
    begin
      item := TSumOfID(sumList[i]);
      // ... und im Memo ausgeben
      memLog.Lines.Add('IDNr ' + IntToStr(item.IDNr) + ' Summe Betrag: ' + FloatToStr(item.Sum));
    end;
  finally
    sumList.Free;
  end;
end;
wp_xyz
 
Beiträge: 2249
Registriert: 8. Apr 2011, 08:01

Beitragvon BitRausch » 7. Jun 2017, 14:38 Re: Welche Unit/Komponente für CSV Dateien

Oh wow ... sehr cool... :D
Probiere ich gleich mal aus...
Schöne Aufgabe für mich ...
Wäre für mich Grundsätzlich die Frage ob ich den Record in meinem Programm nicht als Object bzw. Klasse definiere ...
Aber noch fehlen mir die Basics im OOP...lese viel dazu. Das Gleiche gilt auch für Pointer und Co...

Also bei mir hast Du ein paar Biere gut wp :lol:

PS. Von früher kenne ich noch die Bücher von Marco Cantu ... kann man diese (noch) benutzen?

Edit: Mein Code funktioniert auch nur wenn die Liste nach IDNr sortiert ist. Wenn nicht funktioniert sie auch nicht...
BitRausch
 
Beiträge: 50
Registriert: 30. Mai 2017, 08:32

Beitragvon wp_xyz » 7. Jun 2017, 15:14 Re: Welche Unit/Komponente für CSV Dateien

BitRausch hat geschrieben:Wäre für mich Grundsätzlich die Frage ob ich den Record in meinem Programm nicht als Object bzw. Klasse definiere ...

Bei kleinen Programmen ist das im Grunde egal. Allerdings kannst du mit Klassen Code, der sonst in deinem Programm untergebracht ist, in die Klasse auslagern. Das siehst du auch in meinem letzten Beispiel: da ist das Aufsummieren in der Schleife nur noch ein Einzeiler, weil alles Relevante in den Klassen geschieht. (Heutzutage kann man aber auch Records Methoden zuordnen, so dass die Unterschiede verschwimmen.)

BitRausch hat geschrieben:Von früher kenne ich noch die Bücher von Marco Cantu ... kann man diese (noch) benutzen?

Wahrscheinlich. Nur sollte es eine ältere Ausgabe sein, die neuen Ausgaben beschreiben hauptsächlich neue Features von Delphi, die in Lazarus/FPC evtl noch nicht vorkommen.

BitRausch hat geschrieben:Mein Code funktioniert auch nur wenn die Liste nach IDNr sortiert ist. Wenn nicht funktioniert sie auch nicht...

Bei meinem Code ist die Sortierung egal. Du kannst aber die Summenliste für die Ausgabe sortieren:
Code: Alles auswählen
procedure CompareID(item1, item2: Pointer): Integer;
begin
  Result := CompareValue(TSumOfID(item1).ID, TSumOfID(item2).ID));   // CompareValue steht in der unit "math"
end;
 
procedure CompareSumDesc(item1, item2: Pointer): Integer;
begin
  Result := CompareValue(TSumOfID(item2).Sum TSumOfID(item1).Sum));     // Absteigende Sortierung durch Vertauschen von 1 und 2
end;
 
...
// Nach ID sortieren
sumList.Sort(@CompareID);
 
// oder:
// Nach Summe (fallende) sortieren
sumList.Sort(@CompareSumDesc)

Die von TList geerbte Sortier-Routine vergleicht immer zwei Listeneinträge, dazu ist die Compare-Funktion gedacht; sie liefert -1, wenn Item1>Item2 ist, +1 wenn Item2 > Item1, und 0 wenn Item1 = Item2). Auf diese Weise kann man jede beliebige Sortierung erreichen.
wp_xyz
 
Beiträge: 2249
Registriert: 8. Apr 2011, 08:01

Beitragvon BitRausch » 7. Jun 2017, 15:52 Re: Welche Unit/Komponente für CSV Dateien

eine Verständnisfrage:
in Deinem Code

Code: Alles auswählen
 
// Eine ID suchen (nicht optimiert; bei einer sortierten Liste könnte man z.B.
// mit einem optimierten Suchalgorithmus schneller suchen)
function TSumList.FindIndexOfID(AIDNr: Integer): Integer;
begin
  for Result := 0 to Count-1 do
    if TSumOfID(Items[Result]).IDNr = AIDNr then
      exit;
  // Nicht gefunden --> -1 zurückgeben
  Result := -1;
end;
 


woher kommt "Count"? Dachte das wäre eine Methode von TObjectList, aber da sehe ich keine...
Ahh :idea: ... Vererbung... kommt von TList...
BitRausch
 
Beiträge: 50
Registriert: 30. Mai 2017, 08:32

Beitragvon BitRausch » 3. Jul 2017, 08:20 Re: Welche Unit/Komponente für CSV Dateien

Leider kam ich in letzter Zeit nicht dazu mich weiter mit dem Programm zu beschäftigen...
Jetzt sitze ich aber wieder dran und versuche es die nächsten Tage fertig zu bekommen...

Jetzt zerbreche ich mir den Kopf über ein Problem mit dem ich nicht gerechnet hatte:

Wie kann ich mit TCSVParser prüfen ob der Datensatz komplett ist?. D,h. Ein Datensatz muss eine feste Anzahl von Feldern habe. Wenn nicht dann soll die weitere Verarbeitung abgebrochen werden.
In csvreadwrite Code bin ich auf das property MaxColCount gestoßen und nahm an das es die maximale Anzahl der Felder (Columes) pro Zeile enthält. Dies scheint aber nicht der Fall zu sein - oder ich verstehe es nicht richtig??

Beschreibung in csvreadwrite:
// The maximum number of columns found in the stream:
property MaxColCount: Integer read FMaxColCount;

Wenn ich unser kleines Beispiel nehme...:
Code: Alles auswählen
 
procedure TForm1.LeseDatei(const AFileName: String);
const
  BLOCK_SIZE = 1000;
var
  counter: Integer;
  csv: TCsvParser;
  stream: TFileStream;
 
begin
  SetLength(Data, 0);
  // Counter zählt die eingelesenen Zeilen
  counter := 0;
  csv := TCSVParser.Create;
  try
    csv.Delimiter := ';';
    stream := TFileStream.Create(AFileName, fmOpenRead + fmShareDenyWrite);
    try
      csv.SetSource(stream);
      while csv.ParseNextCell do begin
        if csv.CurrentRow <> counter then   // Neue Zeile erreicht
        begin
          inc(counter);
          if ((counter - 1) mod BLOCK_SIZE = 0) then
            // Das Datenarray wird in "Häppchen" der Größe BLOCK_SIZE dimensioniert
            SetLength(Data, Length(Data) + BLOCK_SIZE);
        end;
        if counter > 0 then
          with Data[counter-1] do   // -1, um die Titelzeile zu überspringen
            case csv.CurrentCol of
              0: begin
                   if IstPflichtFeld(0, csv.CurrentCellText) then
                      IDNr := StrToInt(csv.CurrentCellText)
                   else
                      LogError('Zeile '+ IntToStr(counter) + ' IDNr ist ein Pflichtfeld und darf nicht leer sein');
                 end;
              1:  begin
                   if IstPflichtFeld(1, csv.CurrentCellText) then
                      LfdNr := StrToInt(csv.CurrentCellText)
                   else
                      LogError('Zeile '+ IntToStr(counter) + ' LfdNr ist ein Pflichtfeld und darf nicht leer sein');
                  end;
              2: begin
                   if IstPflichtFeld(2, csv.CurrentCellText) then
                      Zahl := ThSepStrToFloat(csv.CurrentCellText, FormatSettings.ThousandSeparator)
                   else
                      LogError('Zeile '+ IntToStr(counter) + ' Zahl ist ein Pflichtfeld und darf nicht leer sein');
                 end;
              3: begin
                   if IstPflichtFeld(3, csv.CurrentCellText) then
                      Datum := StrToDate(csv.CurrentCelLText)
                   else
                      LogError('Zeile '+ IntToStr(counter) + ' Datum ist ein Pflichtfeld und darf nicht leer sein');
                 end;
              4: Text := Uppercase(csv.CurrentCellText);
 
              5: begin
                   if IstPflichtFeld(5, csv.CurrentCellText) then
                      Betrag :=  ThSepStrToFloat(csv.CurrentCellText, FormatSettings.ThousandSeparator)
                   else
                      LogError('Zeile '+ IntToStr(counter) + ' Betrag ist ein Pflichtfeld und darf nicht leer sein');
                 end;
              6: begin
                   if IstPflichtFeld(6, csv.CurrentCellText) then
                      Bedingung1 := Uppercase(csv.CurrentCellText)
                   else
                      LogError('Zeile '+ IntToStr(counter) + ' Bedingung1 ist ein Pflichtfeld und darf nicht leer sein');
                 end;
              7: begin
                   Bedingung2 := Uppercase(csv.CurrentCellText);
                   if (Bedingung1 = '') And (Bedingung2 <> '') then
                      LogError('Zeile '+ IntToStr(counter) + ' Bedingung1 ist leer');
                 end;
            end;
//            FieldCount := 0;
            //if (csv.MaxColCount < 8) then
//            LogError(IntToStr(csv.CurrentCol)+'Spalten '+' in Zeile '+IntToStr(counter));
      end;
//      FieldCount:= csv.MaxColCount;
      SetLength(Data, counter);
      LogError('======================================');
      LogError('Zeilen eingelesen: '+ IntToStr(counter));
      LogError('Spalten pro Zeile eingelesen: '+ IntToStr(csv.MaxColCount));
    finally
      stream.Free;
    end;
  finally
    csv.Free;
  end;
end;                   
 
 


...dann frage ich mich wie hier die Prüfung auf Vollständigkeit des Satze funktioniert...
Gibt es eine andere Möglichkeit die Anzahl der Felder pro Zeile raus zubekommen?
Müsste ich die Zeile 2mal lesen?

Über einen Denkanstoß würde ich mich sehr freuen...
BitRausch
 
Beiträge: 50
Registriert: 30. Mai 2017, 08:32

Beitragvon wp_xyz » 3. Jul 2017, 08:53 Re: Welche Unit/Komponente für CSV Dateien

Du kannst noch eine zweite Zählvariable (ColInRow) einführen, nun für die Spalten pro Zeile. Diesmal kein Code, sondern nur Beschreibung:
  • Setze eine weitere Variable MaxCols auf 0
  • Beginne mit ColInRow = 0.
  • In der while csv.ParseNextCell, im Fall csvCurrentRow = counter, also solange eine Zeile Zelle für Zelle abgearbeitet wird, erhöhst du ColInRow um 1.
  • Im Fall csvCurrentRow <> counter, also wenn eine neue Zeile begonnen wurde, musst du unterscheiden:
    - Nach dem Einlesen der 1. Zeile ist MaxCols noch 0. Setze jetzt MaxCols auf ColInRow - nun weißt du, wieviele Spalten vorhanden sein müssen.
    - Bei den anderen Zeilen prüfst du, ob ColInRow gleich der erwarteten Spaltenzahl, also MaxCols ist. Wenn nicht --> Fehler.
wp_xyz
 
Beiträge: 2249
Registriert: 8. Apr 2011, 08:01

Beitragvon BitRausch » 3. Jul 2017, 17:16 Re: Welche Unit/Komponente für CSV Dateien

Klasse!! Danke für den Denkanstoß!!
Hab es gelöst und jetzt wo ich es sehe kann ich mir nur an den Kopf greifen ... :roll:
Ist alles noch nicht schön aber selten :lol:

Code: Alles auswählen
 
Globale Variable
LineError : Boolean = false;
 
....
 
procedure TForm1.LeseDatei(const AFileName: String);
const
  BLOCK_SIZE = 1000;
  FIX_COL = 7 //Anzahl der erwarteten Spalten in einem Datensatz
 
 var
   ....
   ColInRow : Integer;
 
begin
  SetLength(Data, 0);
  // Counter zählt die eingelesenen Zeilen
  counter := 0;
  csv := TCSVParser.Create;
  ColInRow := 0;
  LineError := false;
  try
    csv.Delimiter := ';';
    stream := TFileStream.Create(AFileName, fmOpenRead + fmShareDenyWrite);
    try
      csv.SetSource(stream);
      while csv.ParseNextCell do begin
        inc(ColInRow);   //Zähler für Spalten in Zeile um 1 erhöhen
        if csv.CurrentRow <> counter then   // Neue Zeile erreicht
        begin
          inc(counter);
          if (ColInRow-1) < FIX_COL then               // Wenn Anzahl Spalten in Zeile kleiner als die erwartete Spalten.
          begin
           memLog.Lines.Add('Fehlerhafter Datensatz gefunden - Verarbeitung abgebrochen!');
            LogError('Unvollständiger Datensatz gefunden in Zeile: '+ IntToStr(counter));
            LineError := true;
            exit;
          end
          else
            ColInRow:= 0;   // Zähler für Spalten zurücksetzen
          if ((counter - 1) mod BLOCK_SIZE = 0) then
            // Das Datenarray wird in "Häppchen" der Größe BLOCK_SIZE dimensioniert
            SetLength(Data, Length(Data) + BLOCK_SIZE);
        end
 
...
 
 
BitRausch
 
Beiträge: 50
Registriert: 30. Mai 2017, 08:32

Beitragvon BitRausch » 9. Aug 2017, 11:54 Re: Welche Unit/Komponente für CSV Dateien

@wp_xyz

wollte nochmal eine Rückmeldung geben.
Mein Programm habe ich fast fertig. Es ist nicht ganz so OOPig geworden - noch viel zu sehr "old School" prozedural geschrieben.
Aber für meine Version 1.0 bin ich doch recht zufrieden.
Ich wäre aber nicht soweit gekommen ohne Deine tolle Hilfe, Denkanstöße und dem kleinem Crash Kurs...
Dafür danke ich Dir sehr! Echt toll!!

Ich habe noch einige Fragen aber die würden nicht mehr zu diesem Thema passen. Werde dann eigene Threads aufmachen.

PS. Wie markiert man ein Thread als gelöst??
BitRausch
 
Beiträge: 50
Registriert: 30. Mai 2017, 08:32

Beitragvon wp_xyz » 9. Aug 2017, 12:10 Re: Welche Unit/Komponente für CSV Dateien

BitRausch hat geschrieben:Wie markiert man ein Thread als gelöst??

Den ersten Beitrag bearbeiten und im Titel den Text "[Gelöst]" an den Anfang schreiben.
wp_xyz
 
Beiträge: 2249
Registriert: 8. Apr 2011, 08:01

• Themenende •
Vorherige

Zurück zu Einsteigerfragen



Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast

cron
porpoises-institution
accuracy-worried