Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - DelimitedText, richtige Schreibweise?


hRb - Fr 11.11.22 21:43
Titel: DelimitedText, richtige Schreibweise?
Ich will ein Stringgrid speichern, jedoch anstellle von Commatext das Trennzeichen '|' nutzen, da das Komma in den Daten selbst vorkommen kann. Meine Procedure mit Komma als Trennzeichen funktioniert und sieht wie folgt aus:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
procedure SaveGrid(StringGrid1: TStringGrid; fileName: string);
var
  iRow: integer;
  sl: TStringList;
begin
  try
    sl := TStringList.Create;
    for iRow := 0 to StringGrid1.RowCount - 1 do
      sl.Add(StringGrid1.Rows[iRow].CommaText);
//    sl.Add(StringGrid1.Rows[iRow].Delimitedtext := '|');
    sl.SaveToFile(fileName);
  finally
     sl.Free;
  end;
end;

Ich habe verschiedene schreibweisen probiert. Immer meckert der Compiler. Wie schreibt man folgende Zeile richtig?

Delphi-Quelltext
1:
2:
3:
sl.Add(StringGrid1.Rows[iRow].Delimitedtext := '|');
sl.Add(StringGrid1.Rows[iRow].Delimitedtext['|']);
sl.Add(StringGrid1.Rows[iRow].Delimitedtext = '|');


ub60 - Fr 11.11.22 23:00

Vielleicht einfach selber schreiben (ohne DelimitedText):


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
procedure SaveGrid(StringGrid1: TStringGrid; fileName: string);
var
  iRow, iCol: integer;
  sl: TStringList;
  s: String;
begin
  try
    sl := TStringList.Create;
    for iRow := 0 to StringGrid1.RowCount - 1 do
      begin
        s:=StringGrid1.Cells[0, iRow];
        for iCol := 1 to StringGrid1.ColCount - 1 do
          s:=s+'|'+StringGrid1.Cells[iCol, iRow];
        sl.Add(s);
      end;
    sl.SaveToFile(fileName);
  finally
     sl.Free;
  end;
end;


ub60


hRb - Fr 11.11.22 23:57

Ja, das ist natürlich möglich. Aber man speichert nicht, wenn nicht auch wieder gelesen werden soll und da ist die "automatische Trennzeichen-Auswertung" schon elegant. Siehe unten

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
procedure LoadFromFile(StringGrid1: TStringGrid; txt: TFileName);
var
  sRows, sCols: TStrings;
  i: Integer;
begin
  sRows := TStringList.Create;
  sRows.LoadFromFile(txt);
  sCols := TStringList.Create;
  sCols.CommaText := sRows[0];
  StringGrid1.ColCount := sCols.Count;
  sCols.Free;
  StringGrid1.RowCount := sRows.Count;
  for i := 0 to Pred(sRows.Count) do
    StringGrid1.Rows[i].CommaText := sRows[i];
  sRows.Free;
end;

also ganz einfach:

Delphi-Quelltext
1:
 StringGrid1.Rows[i].CommaText := sRows[i];                    

Deshalb auch hier die Frage: wie? (für mich als Nichtprofi auch zusätzlich ein Punkt zum weiteren Lernen)


Th69 - Sa 12.11.22 09:08

Hallo,

die Eigenschaft heißt Delimiter [https://docwiki.embarcadero.com/Libraries/Alexandria/de/System.Classes.TStrings.Delimiter] - lies dir auch die Beschreibung zu DelimitedText [https://docwiki.embarcadero.com/Libraries/Alexandria/de/System.Classes.TStrings.DelimitedText] durch.


hRb - So 13.11.22 01:33

Danke, Th69
Diese Beschreibung habe ich auch vorab gelesen. Unter DelimitedText [https://docwiki.embarcadero.com/Libraries/Alexandria/de/System.Classes.TStrings.DelimitedText] steht jedoch nur, dass DelimitedText ein Wert zuzuweisen sei. Ein Beispiel fehlt!
Ich dachte, dass anstelle CommaText, einfach die von mir auskommentierte Zeile zu schreiben wäre.

Unter QuoteChar [https://docwiki.embarcadero.com/Libraries/Alexandria/de/System.Classes.TStrings.QuoteChar] finde ich folgendes Beispiel:

Delphi-Quelltext
1:
MyStringList.QuoteChar := #0;                    

bei mir also

Delphi-Quelltext
1:
s1.QuoteChar:= '|';                    

wird vom Compiler angenommen. Auch die Zeile

Delphi-Quelltext
1:
sx.Add(StringGrid1.Rows[iRow].Delimitedtext;                    

also hier nochmals

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
procedure SaveGrid(StringGrid1: TStringGrid; fileName: string);
var
  iRow: integer;
  sx: TStringList;
begin
  try
    sx := TStringList.Create;
    sx.QuoteChar := '|';
    for iRow := 0 to StringGrid1.RowCount - 1 do
      sx.Add(StringGrid1.Rows[iRow].Delimitedtext);
    sx.SaveToFile(fileName);
  finally
     sx.Free;
  end;
end;

Das Ergebnis beim Trennzeichen ist aber nach wie vor das Komma.
Die weiteren Seiten in der Hilfe zum Thema habe ich alle gelesen: jedoch ohne lauffähiges Beispiel. Hat mir alles nicht geholfen.

Daher nochmals nachgefragt: Wie ändere ich den Delimiter?

Moderiert von user profile iconTh69: URL-Titel hinzugefügt


Th69 - So 13.11.22 07:48

Du weißt doch wie man Eigenschaften setzt:

Delphi-Quelltext
1:
MyStringList.Delimiter := '|';                    

Bei dir also (vor dem Add):

Delphi-Quelltext
1:
StringGrid1.Rows[iRow].Delimiter := '|';                    

PS: QuoteChar ist für das zusätzliche Setzen in Anführungszeichen (oder andere Zeichen), d.h. vor und nach jedem Eintrag. Dies wäre also eine andere Alternative, um Kommas in Texten abzuspeichern und wieder korrekt zu lesen (s.a. CommaText [https://docwiki.embarcadero.com/Libraries/Alexandria/de/System.Classes.TStrings.CommaText]), d.h. dein ursprünglicher Code müßte einwandfrei (auch mit Kommas) funktionieren.


Sinspin - Mo 14.11.22 13:09

Hey,
dank user profile iconTh69 seiner guten Vorarbeit sollte alles zusammen dann etwa so aussehen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
procedure SaveGrid(StringGrid1: TStringGrid; fileName: string);
var
  iRow: integer;
  sl: TStringList;
begin
  try
    sl := TStringList.Create;
    for iRow := 0 to StringGrid1.RowCount - 1 do
    begin
      StringGrid1.Rows[iRow].DelimitedText := '|';
      sl.Add(StringGrid1.Rows[iRow].CommaText);
    end;
    sl.SaveToFile(fileName);
  finally
     sl.Free;
  end;
end;

Dabei wird die TStringList "sl" gelassen wie eingestellt.
Vor dem Auslesen der Eigenschaft CommaText, der Zeile des TStringGrid müssen alle Eigenschaften gesetzt sein die CommaText verwendet um den formatierten String zu erzeugen.

Übrigens ist es keine gute Idee "#0" an irgend einer Stelle als Stringtrenner zu verwenden. Das kann(wird) zu lustigen Fehlern führen wenn man sein Programm mal aus der Delphiwelt heraus lässt.


Th69 - Mo 14.11.22 14:37

Hallo Sinspin,

da hast du dich aber noch etwas vertan - es muß so aussehen:

Delphi-Quelltext
1:
2:
StringGrid1.Rows[iRow].Delimiter := '|';
sl.Add(StringGrid1.Rows[iRow].DelimitedText);

;-)

PS: Bzw. persönlich würde ich es (laufzeit-optimiert) so schreiben:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
var
  rowStrings: TStrings;
begin
  {$ ... $}

  rowStrings := StringGrid1.Rows[iRow];
  rowStrings.Delimiter := '|';
  sl.Add(rowStrings.DelimitedText);

  {$ ... $}
end


hRb - Mo 14.11.22 15:25

Also so wie im Beispiel angegeben funktioniert es nicht.
(Habe zunächst Schleifenvariable korrigiert in for j:=cFixRow = 1 to .., da eine Überschriftenzeile. Aber das nur nebenbei)
Meine Stringliste sah vor dem Speichern wie folgt aus (s. Image 4)
Nach der Procedure waren alle Einträge aus meiner Stringliste gelöscht und in Spalte 1 war überall | (s.Image5)
In der Textdatei in die ich die Liste speichere sieht es ähnlich aus:

Quelltext
1:
2:
3:
4:
5:
6:
|,,,,,,,,
|,,,,,,,,
|,,,,,,,,
|,,,,,,,,
|,,,,,,,,
|,,,,,,,,

Anmerkung: Habe mich auch gewundert, dass StringGrid1.Rows[iRow].DelimitedText := '|'; innerhalb der Schleife und vor jedem Add steht und dann doch

Moderiert von user profile iconTh69: Delphi-Tags hinzugefügt


hRb - Mo 14.11.22 15:27

Oh, jetzt sehe ich gerade den Korrektureintrag von Th69. Werde es testen


Th69 - Mo 14.11.22 16:33

Hat es geklappt?

user profile iconhRb hat folgendes geschrieben Zum zitierten Posting springen:
... und dann doch

Da fehlt das Ende des Satzes...


Sinspin - Mo 14.11.22 18:18

user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Hallo Sinspin,

da hast du dich aber noch etwas vertan - es muß so aussehen:

Delphi-Quelltext
1:
2:
StringGrid1.Rows[iRow].Delimiter := '|';
sl.Add(StringGrid1.Rows[iRow].DelimitedText);

;-)

...und wie Du recht hast.
Wenn man versucht mal was so nebenbei zu machen. :oops:

user profile iconhRb hat folgendes geschrieben Zum zitierten Posting springen:
Anmerkung: Habe mich auch gewundert, dass StringGrid1.Rows[iRow].DelimitedText := '|'; innerhalb der Schleife und vor jedem Add steht

Jede Row ist ja eine extra TStringList, von der niemand weis ob "Delimiter" richtig steht. "DelimitedText" zu überschreiben ist hingegen Blödsinn da es den Inhalt des StringGrids killt.
Ich versuche es nochmal, und jetzt richtig. Hoffentlich.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
procedure SaveGrid(StringGrid1: TStringGrid; fileName: string);
var
  iRow: integer;
  sl: TStringList;
begin
  try
    sl := TStringList.Create;
    for iRow := 0 to StringGrid1.RowCount - 1 do
    begin
      StringGrid1.Rows[iRow].Delimiter := '|';
      sl.Add(StringGrid1.Rows[iRow].DelimitedText);
    end;
    sl.SaveToFile(fileName);
  finally
     sl.Free;
  end;
end;


hRb - Mo 14.11.22 18:37

Sorry, musste Einkaufen. Da wurde wohl ein halbfertiger Text gesendet.
So, in dieser Form funktioniert es:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
procedure SaveGrid(StringGrid1: TStringGrid; fileName: string);
var
  iRow: integer;
  rowStrings : TStringList;
begin
  try
    rowStrings := TStringList.Create;
    for iRow := 1 to StringGrid1.RowCount - 1 do
    begin
      StringGrid1.Rows[iRow].Delimiter := '|';
      rowStrings.Add(StringGrid1.Rows[iRow].DelimitedText);
    end;
    rowStrings.SaveToFile(fileName);
  finally
     rowStrings.Free;
  end;
end;

procedure TForm1.BilderlisteSpeichern1Click(Sender: TObject);
VAR Dateiname:string;
begin
if FileSaveDialog1.Execute then
  begin
    Dateiname := FileSaveDialog1.FileName;
    SaveGrid(Stringgrid1,Dateiname);
  end;
end;

Anmerkung1: Die optimierte Version rowStrings := StringGrid1.Rows[iRow] ; mag der Compiler nicht (Semikolon ist rot unterstrichen)
Anmerkung2:
Zitat:
Übrigens ist es keine gute Idee "#0" an irgend einer Stelle als Stringtrenner zu verwenden.
Da stimme ich zu, aber ich habe den Befehl einfach aus den Beispielen von Embarcadero übernommen.
Danke, Ihr seid schneller als ich. Dafür ist mein Kühlschrank wieder voll :D


Th69 - Mo 14.11.22 18:50

Das würde mich jetzt aber doch interessieren, warum mein anderer Code nicht funktioniert (denn denselben Code mehrfach zu wiederholen, sollte man ja vermeiden). Welche genaue Fehlermeldung gibt es denn beim Kompilieren?

Edit: Sehe jetzt gerade, daß du ja jetzt sl nicht mehr benutzt, sondern stattdessen rowStrings - du brauchst schon 2 verschiedene Variablen dafür.


hRb - Di 15.11.22 00:40

Die Fehlermeldung lautet: Inkompatible Typen: TStringlist und TStrings
Zitat:
Sehe jetzt gerade, daß du ja jetzt sl nicht mehr benutzt, sondern stattdessen rowStrings - du brauchst schon 2 verschiedene Variablen dafür.

Die Variable s1 (1=Zahl) wollte ich künftig nicht mehr nutzen. Im Delphi-Editor gibt es leicht Verwechslung mit sl (1=Zahl) und sl (= kleiner Buchstabe L)
2 verschiedene Variablen? Ok, wenn dies optimiert ist!
Läuft jetzt auch so (schwere Geburt, Danke fürs "Mitpressen")

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
procedure SaveGrid(StringGrid1: TStringGrid; fileName: string);
var
  iRow: integer;
  rowStrings : TStrings;
  sL:Tstringlist;
begin
  try
    sL := TStringList.Create;
    for iRow := 1 to StringGrid1.RowCount - 1 do
    begin
      rowStrings := StringGrid1.Rows[iRow];
      rowStrings.Delimiter := '|';
      sL.Add(rowStrings.DelimitedText);
    end;
    sL.SaveToFile(fileName);
  finally
     sL.Free;
  end;
end;


Th69 - Di 15.11.22 09:46

Optimiert in dem Sinne, daß nur einmal (pro Schleifendurchgang) auf das Rows-Array zugegriffen wird (die zusätzliche lokale Variable wird Delphi wohl im Release-Modus als Register benutzen, d.h. keine Stackvariable dafür anlegen) - dies ist zwar laufzeittechnisch nur eine Mikrooptimierung, ich finde den Code aber so schöner zu lesen, so daß je Objekt nur ein Zugriff darauf passiert (bei einer Funktion würde man diese ja auch nicht mehrfach aufrufen).