Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi CSV Dateien einlesen (https://www.delphipraxis.net/174012-csv-dateien-einlesen.html)

DelphiRacer 28. Mär 2013 10:05


CSV Dateien einlesen
 
Hallo Community,

das einlesen von csv - Dateien in Delphi 2007 Prof. funktionierte tadellos.
Jetzt mit XE3 ???
Alles versucht, wer kann helfen...

Problem ist das keine Werte ins Grid eingetragen werden.
Es kommt keine Fehlermeldung und im Grid Spalte0,Zeile0 stehen Chinesische Zeichen...


Hier die aktuelle Source...
Delphi-Quellcode:

procedure T_Rezepteditor.FormShow(Sender: TObject);
var      z: single;
        x,y: integer;
          t: string;
          i: Integer;
 csvReader : TCSVReader;
     sData : TFileStream;

begin
  // Mit Entertaste weiter
  KeyPreview := True;
  // Dateiname
  t:=Label1.Caption;

  sData := TFileStream.Create(t,fmOpenRead);
  csvReader := TCSVReader.Create (sData, ';');
  csvReader.First;
  try
    StringGrid1.ColCount:=csvReader.ColumnCount;
    y:=-1; // Zeile
    while not csvReader.Eof Do
      begin
        y:=y+1;
        StringGrid1.RowCount:=y+1;
        for x:=0 to csvReader.ColumnCount - 1 Do
          StringGrid1.Cells[x,y]:=(csvReader.Columns[x]);
        csvReader.Next;
      end;
  finally
    csvReader.Free;
    sData.Free;
  end;
end;
:pale:

DeddyH 28. Mär 2013 10:17

AW: CSV Dateien einlesen
 
Vermutlich ist TCSVReader nicht unicode-fähig.

DelphiRacer 28. Mär 2013 10:34

AW: CSV Dateien einlesen
 
Die Vermutung liegt nahe...
Gibt es Alternativen ?

DeddyH 28. Mär 2013 10:38

AW: CSV Dateien einlesen
 
Ist das das Ding aus der DP? Dann liegt der Source doch vor, müsste man halt mal reinschauen und anpassen.

Steku 28. Mär 2013 10:42

AW: CSV Dateien einlesen
 
Vielleicht das hier?
http://www.delphipraxis.net/110025-c...dateien-4.html

Gruß
Steku

DelphiRacer 28. Mär 2013 10:46

AW: CSV Dateien einlesen
 
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.

DelphiRacer 28. Mär 2013 10:56

AW: CSV Dateien einlesen
 
Zitat:

Zitat von Steku (Beitrag 1209203)

Genau der ...

DeddyH 28. Mär 2013 11:01

AW: CSV Dateien einlesen
 
Das müsste ich mir mal ganz in Ruhe anschauen. Wenn es schnell gehen soll, ersetze doch testhalber mal alle PChar durch PAnsiChar und alle Char durch AnsiChar. Funktioniert es dann?

p80286 28. Mär 2013 11:08

AW: CSV Dateien einlesen
 
Zitat:

Zitat von DeddyH (Beitrag 1209211)
Das müsste ich mir mal ganz in Ruhe anschauen. Wenn es schnell gehen soll, ersetze doch testhalber mal alle PChar durch PAnsiChar und alle Char durch AnsiChar. Funktioniert es dann?

Die (Ansi)strings nicht vergessen!

Gruß
K-H

DeddyH 28. Mär 2013 11:10

AW: CSV Dateien einlesen
 
Stimmt, die habe ich überlesen.

DelphiRacer 28. Mär 2013 11:22

AW: CSV Dateien einlesen
 
Zitat:

Zitat von p80286 (Beitrag 1209213)
Zitat:

Zitat von DeddyH (Beitrag 1209211)
Das müsste ich mir mal ganz in Ruhe anschauen. Wenn es schnell gehen soll, ersetze doch testhalber mal alle PChar durch PAnsiChar und alle Char durch AnsiChar. Funktioniert es dann?

Die (Ansi)strings nicht vergessen!

Gruß
K-H

Das habe ich gemacht, leider kommen eine Menge Fehlermeldungen:
(logisch)
fDelimiter := FormatSettings.ListSeparator (AnsiChar, Char)

...der .Listseparator ist ein (Char)
muss doch einiges umgeschrieben werden.
Ziehe mir gerade die Hilfe von E-XE3 heran.


Ein Auszug sagt:

Aufrufe von Read/ReadBuffer-Methoden von TStream
Überprüfen Sie die Aufrufe von TStream.Read/ReadBuffer, wenn Strings oder Zeichen-Arrays verwendet werden. Sehen Sie sich das folgende Beispiel an:

var
S: string;
L: Integer;
Stream: TStream;
Temp: AnsiString;
begin
// Vorhandener Code ist falsch, wenn String UnicodeString ist
Stream.Read(L, SizeOf(Integer));
SetLength(S, L);
Stream.Read(Pointer(S)^, L);

// Richtig für Unicode-String-Daten
Stream.Read(L, SizeOf(Integer));
SetLength(S, L);
Stream.Read(Pointer(S)^, L * SizeOf(Char)); // <<-- Geben Sie die Puffergröße in Byte an

// Richtig für Ansi-String-Daten
Stream.Read(L, SizeOf(Integer));
SetLength(Temp, L); // <<-- Temporären AnsiString verwenden
Stream.Read(Pointer(Temp)^, L * SizeOf(AnsiChar)); // <<-- Geben Sie die Puffergröße in Byte an
S := Temp; // <<-- String auf Unicode erweitern
end;


p80286 28. Mär 2013 11:47

AW: CSV Dateien einlesen
 
Zitat:

Zitat von DelphiRacer (Beitrag 1209220)
Das habe ich gemacht, leider kommen eine Menge Fehlermeldungen:
(logisch)
fDelimiter := FormatSettings.ListSeparator (AnsiChar, Char)

...der .Listseparator ist ein (Char)
muss doch einiges umgeschrieben werden.
Ziehe mir gerade die Hilfe von E-XE3 heran.


Ein Auszug sagt:

[COLOR="Blue"] Aufrufe von Read/ReadBuffer-Methoden von TStream
Überprüfen Sie die Aufrufe von TStream.Read/ReadBuffer, wenn Strings oder Zeichen-Arrays verwendet werden. Sehen Sie sich das folgende Beispiel an:................

Hab ich da irgendetwas verpasst?
Der Listseperator ist ein Char was soll dann das "(Ansichar, Char)"?

Irgendwann später wirst Du auch die Daten einlesen, da muß der Puffer entsprechend angepasst werden (
Delphi-Quellcode:
sizeof(char)
) soweit ganz richtig, aber was haben diese beiden sachen miteinander zu tun?

Und was ist wenn Du CSV-Dateien mit 16 und 8 Bit-Zeichen hast?

Gruß
K-H

DelphiRacer 28. Mär 2013 12:40

AW: CSV Dateien einlesen
 
Zitat:

Zitat von p80286 (Beitrag 1209223)
Zitat:

Zitat von DelphiRacer (Beitrag 1209220)
Das habe ich gemacht, leider kommen eine Menge Fehlermeldungen:
(logisch)
fDelimiter := FormatSettings.ListSeparator (AnsiChar, Char)

...der .Listseparator ist ein (Char)
muss doch einiges umgeschrieben werden.
Ziehe mir gerade die Hilfe von E-XE3 heran.


Ein Auszug sagt:

[COLOR="Blue"] Aufrufe von Read/ReadBuffer-Methoden von TStream
Überprüfen Sie die Aufrufe von TStream.Read/ReadBuffer, wenn Strings oder Zeichen-Arrays verwendet werden. Sehen Sie sich das folgende Beispiel an:................

Hab ich da irgendetwas verpasst?
Der Listseperator ist ein Char was soll dann das "(Ansichar, Char)"?

Irgendwann später wirst Du auch die Daten einlesen, da muß der Puffer entsprechend angepasst werden (
Delphi-Quellcode:
sizeof(char)
) soweit ganz richtig, aber was haben diese beiden sachen miteinander zu tun?

Und was ist wenn Du CSV-Dateien mit 16 und 8 Bit-Zeichen hast?

Gruß
K-H

Das diente nur zur Darstellung,
das der fDelimiter=AnsiChar, und Listseperator=Char ist.
(Darstellung war verwirrend, einfach vergessen)...

Da ein einfaches Umschreiben in diesem Falle nicht möglich ist...

DeddyH 28. Mär 2013 12:43

AW: CSV Dateien einlesen
 
Aber ein Cast sollte doch funktionieren:
Delphi-Quellcode:
var
  c: Ansichar;
  d: char;
begin
  c := 'A';
  d := Char(c);

DelphiRacer 30. Mär 2013 09:24

AW: CSV Dateien einlesen
 
Zitat:

Zitat von DeddyH (Beitrag 1209231)
Aber ein Cast sollte doch funktionieren:
Delphi-Quellcode:
var
  c: Ansichar;
  d: char;
begin
  c := 'A';
  d := Char(c);

Das schon...
dafür eine Menge andere Fehler.

Es musste eine schnelle Lösung her.
Habe das Ganze jetzt so gelöst...
Liest "nur" die selbst gebastelten CSV-File.
Also "Text";daten;"Text";Text;Daten

Delphi-Quellcode:

uses
  function Read_CSV(SG: TStringGrid; Dateiname: String): Boolean;

.
.
procedure T_Zutaten.FormShow(Sender: TObject);
var      t: string;

begin
  t:='C:\DB\Zutaten.csv';
  Read_CSV (StringGrid1,t);
end.
.
.
// CSV File Einlesen ===========================================================
function Read_CSV (SG: TStringGrid; Dateiname: String): Boolean;
var
 OutputFile: TextFile;
sOutputName: string;
          l: integer;
          c: integer;
        x,y: integer;
        s,t: string;

begin
  // Startwerte Reihe StringGrid
  y:=-1;

  sOutputname:=Dateiname;
  AssignFile(OutputFile, sOutputName);
  Reset(OutputFile);
  try
    while not Eoln(outputFile) do
    begin
      x:=0;
      y:=y+1;
      SG.RowCount:=y+1;
      ReadLn(OutputFile, t);
      l:=length(t);
      s:='';
      for c := 1 to l do
        begin
          if t[c]<> ';' then if t[c]<> '"' then s:=s+t[c];
          // Trennzeichen gefunden
          if t[c] = ';' then
          begin
            if length(s)=0 then s:=' ';
            SG.Cells[x,y]:=s;
            s:='';
            x:=x+1;
            if y=0 then SG.ColCount:=x;
          end;
          // Zeilen Ende
          if c = l then
          begin
            if length(s)=0 then s:=' ';
            SG.Cells[x,y]:=s;
            s:='';
            x:=x+1;
            if y=0 then SG.ColCount:=x;
          end;
        end;
    end;
  finally
    CloseFile(OutputFile);
    Result:=True;
  end;
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:52 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