Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Software-Projekte der Mitglieder (https://www.delphipraxis.net/26-software-projekte-der-mitglieder/)
-   -   CSVViewer - Zum Betrachten von CSV-Dateien (https://www.delphipraxis.net/158355-csvviewer-zum-betrachten-von-csv-dateien.html)

Luckie 14. Feb 2011 16:21

CSVViewer - Zum Betrachten von CSV-Dateien
 
Liste der Anhänge anzeigen (Anzahl: 2)
Nichts großes, weltbewegendes. Nur ein kleines Programm, um CSV-Dateien betrachten zu können, ohne dass man immer gleich Excel oder OpenOffice starten muss. So was wollte ich schon immer mal für die Backupdateien meiner Adressdatenbank machen und jetzt habe ich es gemacht. Es war eigentlich nur für mich zu privat Zwecken gedacht, aber eventuell findet es ja noch jemand anders nützlich, wenn nicht das Programm, dann vielleicht den Code, obwohl auch dieser war in seiner Grundform in zwanzig Minuten hingehackt.

Das Programm sollte selbsterklärend sein. Mit einem Rechtsklick auf den Listview öffnet sich ein Kontextmenü, mit dem man entweder die eine Zelle/Feld in die Zwischenablage kopieren kann oder die ganze Zeile.

vergessen 14. Feb 2011 19:34

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Hi Luckie,

vielen Dank für den Quelltext! War schon immer zu faul für sowas :oops: !

Mein D7pe kennt die Zeile (in TfrmMain.FillListview) mit
Delphi-Quellcode:
Delimitedline.StrictDelimiter := True;
nicht und mag die Zeile für das automatische Spaltenbreitensetzen mit
Delphi-Quellcode:
lvCSVFile.Columns.Items[j].Width := ColumnTextWidth;
nicht (Absturz mit "Listenindex überschreitet das Maximum (x)").

Abhilfe: Beide Zeilen (in TfrmMain.FillListview) auskommentieren und nach
Delphi-Quellcode:
Progressbar1.Position := 0;
die Zeile
Delphi-Quellcode:
for i:=0 to lvCSVFile.Columns.count -1 do lvCSVFile.Columns[i].width:= -1;
einfügen.


MfG

Luckie 14. Feb 2011 19:37

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Entwickelt hab eich mit D2006. Und ColumnTextWidth ist nur eine Konstante für -1.

vergessen 14. Feb 2011 19:47

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Delphi-Quellcode:
lvCSVFile.Columns.Items[j].Width := -1;
statt
Delphi-Quellcode:
lvCSVFile.Columns.Items[j].Width := ColumnTextWidth;
führt in D7pe zum gleichen Absturz :oops:

MfG

mkinzler 14. Feb 2011 19:48

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Es ist ja auch egal, ob man eine Konstante niummt oder den Wert direkt zuweist.

Luckie 14. Feb 2011 19:50

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Jetzt stellt sich die Frage, warum das bei mir nicht zum Absturz führt. Ist deine CSV-Datei eventuell nicht in Ordnung oder so?

Sir Rufo 14. Feb 2011 20:18

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Leider kann der CSVViewer nicht alle CSV-Dateien nach RFC 4180 öffnen

Folgendes ist nach RFC ein gültige CSV-Datei
Code:
ID;TEXT
1;Test
2;"Test"
3;"Test""Test"
4;"Test
Test
Test"
5;Last
und sollte folgendes ergeben
Code:
ID TEXT
 1 Test
 2 Test
 3 Test"Test
 4 Test
   Test
   Test
 5 Last
Wenn man mit dem "Microsoft Text-Treiber" die CSV öffnet, dann wird die auch korrekt verarbeitet

Luckie 14. Feb 2011 20:23

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Was macht mein Programm daraus?

Sir Rufo 14. Feb 2011 20:33

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Zitat:

Zitat von Luckie (Beitrag 1081708)
Was macht mein Programm daraus?

sowas
Code:
ID    TEXT
===== =========
1     Test
2     Test
3     Test"Test
4     Tes
Test
Test"
5     Last

Luckie 14. Feb 2011 20:36

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Dann ist das ein Bug von Delphi, dass sich TStringList nicht an das RFC hält. Lösung wäre, die Datei selber zu parsen, aber dazu war ich zu faul. Allerdings, man könnte noch mal probieren, was Explode daraus macht.

Sir Rufo 14. Feb 2011 20:39

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Zitat:

Zitat von Luckie (Beitrag 1081710)
Dann ist das ein Bug von Delphi, dass sich TStringList nicht an das RFC hält. Lösung wäre, die Datei selber zu parsen, aber dazu war ich zu faul. Allerdings, man könnte noch mal probieren, was Explode daraus macht.

Öffne die Datei über eine ADOConnection und der Drops ist gelutscht
Allerdings musst du dafür die CSV-Datei entweder in ein temp. Verzeichnis kopieren, oder
die Schema.ini Datei in das CSV-Verzeichnis erzeugen

Wobei die erste Variante zu bevorzugen ist :)

Luckie 14. Feb 2011 20:41

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Zitat:

Zitat von Sir Rufo (Beitrag 1081711)
Öffne die Datei über eine ADOConnection und der Drops ist gelutscht

Hm, so einen Aufwand wollte ich eigentlich nicht treiben. Aber angucken kann man es ja mal. hast du irgendwo noch weitere Infos dazu? Mit ADO Verbindungen habe ich noch nie gearbeitet.

Sir Rufo 14. Feb 2011 20:47

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Das ist quasi der gesamte "Aufwand"

Delphi-Quellcode:
con1 : TADOConnection;
tbl1 : TADOTable;
ds1 : TDataSource;
dbg1 : TDBGrid;

tbl1.Connection := con1;
ds1.DataSet := tbl1;
dbg1.DataSource := ds1;
und hier die Arbeit
Delphi-Quellcode:
procedure TForm1.FileOpen1Accept( Sender : TObject );
var
  DataFile :  _FileName; // <- ist ein Spezial-Record für Dateinamen :o)
  SchemaFile : _FileName;
  idx :       Integer;
begin
  if con1.Connected
  then
    con1.Close;

  DataFile           := FileOpen1.Dialog.FileName;
  SchemaFile         := DataFile;
  SchemaFile.FileName := 'schema.ini';

  with TIniFile.Create( SchemaFile ) do
    try
      WriteString( DataFile.FileName, 'Format', 'Delimited(;)' );
    finally
      Free;
    end;

  con1.Provider        := 'MSDASQL';
  con1.ConnectionString := 'Driver={Microsoft Text-Treiber (*.txt; *.csv)};Extensions=asc,csv,tab,txt;';
  con1.DefaultDatabase := DataFile.DirName.FullName;

  tbl1.TableDirect := True;
  tbl1.TableName  := DataFile.FileName;

  tbl1.Open;

end;

Luckie 14. Feb 2011 20:48

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Und als visuelle Komponente habe ich dann einen TTable?

mkinzler 14. Feb 2011 20:49

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Eine TTable ist nicht visuell, eher eine DBGrid

Luckie 14. Feb 2011 20:51

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
OK, dann habe ich noch nicht verstanden wie ich das angezeigt bekomme. Wird dann ein DBGrid mit dem TTable verknüpft?

mkinzler 14. Feb 2011 20:52

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Ja über eine TDataSource

DBGrid->DataSource->DataSet

Sir Rufo 14. Feb 2011 20:53

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Steht doch bei mir im ersten CodeTeil
Ist aber getrennt, weil man das a) auf die Form klatscht und b) die Zuordnung über den OI macht

Luckie 14. Feb 2011 20:55

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Ah ja, jetzt sind mir auch die Bezeichnungen klar. ;)

vergessen 14. Feb 2011 21:47

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Ihr macht mir echt Spaß!

Aus einem kleinen CSVViewer flugs ein Monster mit DatenbankTrallala gemacht :evil:

Und Tschüss :oops:

Sir Rufo 14. Feb 2011 21:58

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Zitat:

Zitat von vergessen (Beitrag 1081725)
Ihr macht mir echt Spaß!

Aus einem kleinen CSVViewer flugs ein Monster mit DatenbankTrallala gemacht :evil:

Und Tschüss :oops:

:shock:
Wie sehen bei dir denn kleine Programme aus?
Einzeiler mit Hallo Welt?

Und alles andere ist dann schon Overkill?

vergessen 14. Feb 2011 22:15

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Zitat:

Zitat von Sir Rufo (Beitrag 1081728)
Zitat:

Zitat von vergessen (Beitrag 1081725)
Ihr macht mir echt Spaß!

Aus einem kleinen CSVViewer flugs ein Monster mit DatenbankTrallala gemacht :evil:

Und Tschüss :oops:

:shock:
Wie sehen bei dir denn kleine Programme aus?
Einzeiler mit Hallo Welt?

Und alles andere ist dann schon Overkill?

Mit D7pe jedenfalls ohne Datenbankkram...

Bummi 14. Feb 2011 22:16

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
@vergessen
Monster?
Das ist der unaufwändigste und naheliegenste Weg.

vergessen 14. Feb 2011 22:23

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Zitat:

Zitat von Bummi (Beitrag 1081732)
@vergessen
Monster?
Das ist der unaufwändigste und naheliegenste Weg.

Was denn, Deiner Meinung nach?

Von D7pe mal kurz auf D7pro aufrüsten?

Luckies Original-Source lief mit ein paar kleinen Änderungen auch mit D7pe.

Mei mei oh mei...

Sir Rufo 14. Feb 2011 22:26

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Kann aber nicht alle CSV Dateien nach RFC 4180 korrekt verarbeiten.

Wenn du das nachbauen möchtest, dann hast du schon ein kleineres Monster.

Für mich zählt neben DRY auch DRW (Don't Reinvent Wheels)

vergessen 14. Feb 2011 22:46

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Zitat:

Zitat von Sir Rufo (Beitrag 1081735)
Kann aber nicht alle CSV Dateien nach RFC 4180 korrekt verarbeiten.

Wenn du das nachbauen möchtest, dann hast du schon ein kleineres Monster.

Für mich zählt neben DRY auch DRW (Don't Reinvent Wheels)

Mangels DatenbankTrallala in D7pe muß ich wohl ein vorhandenes Rad neu erfinden :evil:

Sollte mit StringReplace wohl zu machen sein, denke ich mir einfach mal...

@Luckie: Unter welcher Lizenz ist/steht Dein "CSVViewer"-Sourcecode?

MfG

Sir Rufo 14. Feb 2011 22:54

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Luckie hat ja (im Kern) eh "nur" die Methoden von TStringList benutzt.

Die kannst du aber nicht nehmen, weil die das nicht unterstützen.
Somit muss ein eigener Parser her, der sich aber in Grenzen hält, weil du eh nur auf nicht mal eine Handvoll Zeichen prüfen mußt.

Delimiter
Quotes
Zeilenende

Das sollte sich eigentlich gut bewerkstelligen lassen :)

vergessen 14. Feb 2011 23:12

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Zitat:

Zitat von Sir Rufo (Beitrag 1081741)
Die kannst du aber nicht nehmen, weil die das nicht unterstützen.

Ahjahhh. Danke für die Aufklärung!

Eine selbstgeschriebene Function reinwurschteln geht wohl neuerdings nicht mehr?

Ahjahhh. Danke für die Aufklärung!

mei oh mei oh mei!

Sir Rufo 14. Feb 2011 23:21

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Zitat:

Zitat von vergessen (Beitrag 1081746)
Eine selbstgeschriebene Function reinwurschteln geht wohl neuerdings nicht mehr?

Ahjahhh. Danke für die Aufklärung!

mei oh mei oh mei!

In TStringList? ... öh ... ja ... ist aber wohl mehr Aufwand als Nutzen, weil du eigenlich nix davon gebrauchen kannst ...
Das Problem ist, dass die TStringList eben stur mit dem Zeilenende arbeitet.

Eine CSV-Datei kann aber innerhalb eines Feldes auch Zeilenumbrüche beinhalten, und da macht die TStringList eben schlapp.

(Die TStringList ist auch nur für das Aufbrechen einer Zeile benutzt worden, nicht für die gesamte CSV-Datei als Ganzes)

Also eine eigene Klasse bauen (TCSVData) und parsen

Hab da mal was auf die Schnelle zusammengehackt:
Delphi-Quellcode:
unit CSVData;

interface

type
  TCSVData = class
  private
    FData : array of array of string;
    FDelim : Char;
    FQuote : Char;
    function GetRows : Integer;
    function GetCols : Integer;
    function GetCell( Row, Col : Integer ) : string;
    procedure SetCell( Row, Col : Integer; const Value : string );
  public
    destructor Destroy; override;
    procedure LoadFromFile( const FileName : string );
    property Cell[Row, Col : Integer] : string
      read  GetCell
      write SetCell;
    property Rows : Integer
      read  GetRows;
    property Cols : Integer
      read  GetCols;
    property Delim : Char
      read  FDelim
      write FDelim;
    property Quote : Char
      read  FQuote
      write FQuote;
  end;

implementation

uses
  Classes;

{ TCSVData }

destructor TCSVData.Destroy;
begin
  SetLength( FData, 0, 0 );
  inherited;
end;

function TCSVData.GetCell( Row, Col : Integer ) : string;
begin
  Result := FData[Row, Col];
end;

function TCSVData.GetCols : Integer;
begin
  if Rows > 0
  then
    Result := Length( FData[0] )
  else
    Result := 0;
end;

function TCSVData.GetRows : Integer;
begin
  Result := Length( FData );
end;

procedure TCSVData.LoadFromFile( const FileName : string );
var
  Data :     TStrings;
  Val :      string;
  MyChar :   Char;
  LastChar : Char;
  QuotePart : Boolean;
  Col :      Integer;
  Row :      Integer;
  MaxCol :   Integer;
begin
  Data := TStringList.Create;
  try
    Data.LoadFromFile( FileName );

    LastChar := #0;
    QuotePart := False;
    Val      := '';
    MaxCol   := 0;
    Col      := 0;
    Row      := 0;

    // Jedes Zeichen durchlaufen
    for MyChar in Data.Text do
      begin
        if ( MyChar = Quote )
        then
          begin
            // QuotePart wechselt den Status
            QuotePart := not QuotePart;

            // Befinden wir uns im QuotePart und das letzte Zeichen
            // war das Quote-Zeichen, dann handelt es sich um eine
            // Verdoppelung und wir hängen das Quote-Zeichen an
            // den Puffer
            if QuotePart and ( LastChar = Quote )
            then
              Val := Val + Quote;
          end
        else if not QuotePart and ( MyChar = Delim )
        then
          begin
            // Sind noch nicht genug Zeilen da ...
            if high( FData ) < Row + 1
            then
              // ... dann auf Verdacht schon mal 10 hinzufügen
              SetLength( FData, Row + 10 );
            // Sind noch nicht genug Spalten da ...
            if high( FData[Row] ) < Col + 1
            then
              // ... dann auf Verdacht schon mal 10 hinzufügen
              SetLength( FData[Row], Col + 10 );
            // Wert eintragen
            FData[Row, Col] := Val;
            // Puffer leeren
            Val := '';
            // Spalte hochzählen
            Inc( Col );
          end
        else if not QuotePart and ( ( MyChar = #13 ) or ( MyChar = #10 ) )
        then
          begin
            // Haben wir CR LF gefunden ...
            if ( MyChar = #10 ) and ( LastChar = #13 )
            then
              begin
                // Sind noch nicht genug Zeilen da ...
                if high( FData ) < Row + 1
                then
                  // ... dann auf Verdacht schon mal 10 hinzufügen
                  SetLength( FData, Row + 10 );
                // Die Anzahl der Spalten steht jetzt fest
                SetLength( FData[Row], Col + 1 );
                // MaxCol merken
                if Col > MaxCol
                then
                  MaxCol := Col;
                // Wert eintragen
                FData[Row, Col] := Val;
                // Puffer leeren
                Val := '';
                // Zeile hochzählen
                Inc( Row );
                // Neue Zeile => erste Spalte
                Col := 0;
              end;
          end
        else
          // Das aktuelle Zeichen an den Puffer hängen
          Val := Val + MyChar;
        // Das letzte Zeichen merken
        LastChar := MyChar;
      end;

    SetLength( FData, Row );

    // Das ist eigentlich nur notwendig, wenn die CSV-Datei falsch aufgebaut ist
    // und unterschiedliche Anzahl von Spalten in den Zeilen aufweist
    // Dieses ist allerdings nicht RFC-konform, aber wir wollen mal nicht so sein
    for Row := low( FData ) to high( FData ) do
      SetLength( FData[Row], MaxCol + 1 );

  finally
    Data.Free;
  end;
end;

procedure TCSVData.SetCell( Row, Col : Integer; const Value : string );
begin
  FData[Row, Col] := Value;
end;

end.

Luckie 15. Feb 2011 14:24

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Danke. Aber im Moment hab eich andere Probleme. Mein Monitor hat schlapp gemacht und die Grafikkrate unterstützt die native Auflösung des neuen Monitores nicht. :evil: Jetzt muss auch erstmal eine neue Grafikkarte für günstig Geld her. :(

Aber ich denke heute Abend habe ich auch das Problem gelöst.

shmia 15. Feb 2011 14:31

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Hallo :hi:
ich habe schon vor einiger Zeit meinen CSV Editor vorgestellt.
Es ist eine MDI-Anwendung und kann CSV korrekt parsen (Quotezeichen werden beachtet).

Nur den Sprung vom Viewer zum echten Editor hat das Tool nie geschafft...

Luckie 15. Feb 2011 14:34

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Den Sprung wird meiner auch nicht schaffen, aber das soll er auch gar nicht.

Sagt mal, warum seid ihr alle so breit? ;)

sakura 12. Nov 2013 09:54

AW: CSVViewer - Zum Betrachten von CSV-Dateien
 
Liste der Anhänge anzeigen (Anzahl: 1)
Danke Luckie, für die Vorarbeit. Ganz praktisch, aber zuuu langsam. Ich habe die Methode FillListview mal dahingehend angepasst, dass die Daten wirklich schnell verarbeitet werden. Jetzt benötigt nur noch die Berechnung der Spaltenbreite eine gewisse Zeit ;)

...:cat:...


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:19 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz