Thema: Delphi CSV Dateien einlesen

Einzelnen Beitrag anzeigen

DelphiRacer

Registriert seit: 28. Nov 2003
Ort: Königsbronn
8 Beiträge
 
Delphi XE3 Professional
 
#6

AW: CSV Dateien einlesen

  Alt 28. Mär 2013, 10:46
Tja dann lasst uns mal schauen...

Was muss angepasst werden?

Delphi-Quellcode:
Unit csCSV;
(******************************************************************************
* CSV Reader Klasse                                                          *
* Liest eine CSV-Datei ein und ermöglicht Zugriff auf die einzelnen Elemente *
* jeder Zeile.                                                              *
* Eine CSV ('Comma Separated Values' oder 'Character Separated Values' ist  *
* ein Format, um Tabellen in einer Text-Datei zu speichern.                  *
* Dabei werden die einzelnen Elemente einer Tabellenzeile durch ein frei    *
* wählbares Zeichen getrennt. In Deutschland ist dies üblicherweise das      *
* Semikolon, im englischsprachigen Raum das Komma (daher der Name).          *
* Strings werden druch Quotes '"' eingeschlossen, ein Quote innerhalb eines  *
* Strings wird verdoppelt.                                                  *
* Beispiel (Trennzeichen';'):                                                *
* "Text";123;"Text mit ""Quotes"" und Semikolon;";;Auch ein Text;345.657    *
*                                                                            *
* Der Code ist so trivial, das ein Copyright nicht lohnt.                    *
*                                                                            *
* Verwendung                                                                *
*  -- Bereitstellen eines Streams, z.B. TFileStream                        *
*                                                                            *
* csvReader := TCSVReader.Create (CSVDAtaStream, ';');                      *
* While not csvReader.Eof Do Begin                                          *
*  For i:=0 to csvReader.ColumnCount - 1 Do                                *
*    Memo.Lines.Add (csvReader.Columns[i]);                                *
*  csvReader.Next;                                                          *
* End;                                                                      *
******************************************************************************)

Interface
Uses System.Classes, System.SysUtils;
Type
  TStringPos = Record
    spFirst: PChar;
    spLen: Integer;
  End;

  TCSVReader = Class
  private
    fBuffer, fPos, fEnd: PChar;
    fSize: Integer;
    fStream: TStream;
    fQuote,
      fDelimiter: Char;
    fAtEOF, fIsEOF: Boolean;
    fColumns: Array Of TStringPos;
    fColumnCount: Integer;
    fEOLChar: Char;
    fEOLLength: Integer;
    Function GetColumnByIndex(Index: Integer): String;
    Procedure SetEOLChar(Const Value: Char);
    Procedure Initialize;
  public
// Wenn kein Delimiter angegeben wird, wird das Listentrennzeichen aus den
// internationalen Einstellungen von Windows verwendet.
    Constructor Create(aStream: TStream; aDelimiter: Char = #0);
    Destructor Destroy; override;
// Bewegt den internen Positionszeiger auf die erste Zeile der Datei
    Procedure First;
// Bewegt den internen Positionszeiger auf die nächste Zeile der Datei
    Procedure Next;
// Liefert TRUE, wenn keine Daten mehr abgerufen werden können.
    Function Eof: Boolean;
// Liefert oder setzt das Trennzeichen
    Property Delimiter: Char read fDelimiter write fDelimiter;
// Liefert oder setzt das Quote-Zeichen
    Property Quote: Char read fQuote write fQuote;
// Liefert oder setzt das EOL-Zeichen (Windows #13, UNIX #10)
    Property EOLChar: Char read fEOLChar write SetEOLChar;
// Liefert oder setzt die Länge der EOL-Zeichen (Windows : 2 [CR+LF], UNIX: 1 [LF])
    Property EOLLength: Integer read fEOLLength write fEOLLength;
// Liefert die Anzahl der Elemente der aktuellen Zeile
    Property ColumnCount: Integer read fColumnCount;
// Liefert die einzelnen Elemente der aktuellen Zeile
    Property Columns[Index: Integer]: String read GetColumnByIndex; default;
  End;
Implementation

{ TCSVReader }

Constructor TCSVReader.Create(aStream: TStream; aDelimiter: Char);
Begin
  fStream := aStream;
  fSize := fStream.Size - fStream.Position + 2;
  fBuffer := GetMemory(fSize);
  fPos := fBuffer;
  aStream.Read(fBuffer^, fSize);
  fEOLChar := #13;
  fEOLLength := 2;
  fBuffer[fSize - 2] := fEOLChar;
  fBuffer[fSize - 1] := #0;
  fEnd := fBuffer + fSize - 1;
  If aDelimiter = #0 Then
    fDelimiter := FormatSettings.ListSeparator
  Else
    fDelimiter := aDelimiter;

  fQuote := '"';
  setLength(fColumns, 100);
  Initialize;
End;

Destructor TCSVReader.Destroy;
Begin
  FreeMemory(fBuffer);
  Inherited;
End;

Function TCSVReader.Eof: Boolean;
Begin
  Result := fIsEOF;
End;

Procedure TCSVReader.First;
Begin
  Initialize;
  Next;
End;

Function TCSVReader.GetColumnByIndex(Index: Integer): String;
Var
  p: PChar;
  i, l: Integer;

Begin
  With fColumns[Index] Do
    If spLen = 0 Then
      Result := ''
    Else If spFirst^ = fQuote Then Begin
      setLength(Result, spLen - 2);
      p := spFirst + 1;
      l := spLen - 2;
      For i := 1 To spLen - 2 Do Begin
        Result[i] := p^;
        If (p^ = fQuote) And (p[1] = fQuote) Then Begin
          dec(l);
          inc(p, 2)
        End
        Else
          inc(p);
      End;
      SetLength(Result, l);
    End
    Else
      SetString(Result, spFirst, spLen);
End;

Procedure TCSVReader.Initialize;
Begin
  fPos := fBuffer;
  fIsEOF := False;
  fAtEOF := False;
  fColumnCount := 0;
End;

Procedure TCSVReader.Next;
Var
  p: PChar;
  pPrev: PChar;

  Procedure _GetString;
  Begin
    Repeat
      inc(p);
      If p^ = fQuote Then
        If p[1] = fQuote Then
          inc(p)
        Else
          break;
    Until False;
    inc(p);
  End;

Begin
  pPrev := fPos;
  p := fPos;
  fColumnCount := 0;
  If fAtEOF Then
    If Eof Then
      Raise exception.Create('Try to read past eof')
    Else Begin
      fIsEOF := True;
      Exit;
    End;
  If p^ = fQuote Then _GetString;
  While p^ <> fEOLChar Do Begin
    If p^ = fDelimiter Then Begin
      If fColumnCount = Length(fColumns) Then
        SetLength(fColumns, 2 * Length(fColumns));
      fColumns[fColumnCount].spFirst := pPrev;
      fColumns[fColumnCount].spLen := p - pPrev;
      inc(fColumnCount);
      inc(p);
      pPrev := p;
      If p^ = fQuote Then _GetString;
    End
    Else
      inc(p);
  End;
  If p <> fPos Then Begin
    If fColumnCount = Length(fColumns) Then
      SetLength(fColumns, Length(fColumns) + 1);
    fColumns[fColumnCount].spFirst := pPrev;
    fColumns[fColumnCount].spLen := p - pPrev;
    inc(fColumnCount);
  End;
  fPos := p;
  If (fPos[1] = #0) Then
    fAtEOF := True
  Else
    inc(fPos, fEOLLength);
End;

Procedure TCSVReader.SetEOLChar(Const Value: Char);
Begin
  If fEOLChar <> Value Then Begin
    fEOLChar := Value;
    fBuffer[fSize - 2] := fEOLChar;
  End;
End;

End.
Role
  Mit Zitat antworten Zitat