Autor Beitrag
florida
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 137

Windows 7 Home Premium, Windows XP Professional, Windows 2000
Delphi 2010 Architect
BeitragVerfasst: Mi 23.10.13 21:12 
Nachdem ich in einer StringList zeilenweise StringReplace aufrufe, werden zwar alle Vorkommnisse ersetzt, aber StringList.Count bleibt auch nach dem Ersetzen genauso wie vor dem Ersetzen gleich, obwohl bestimmte Zeichen zu Zeilenumbruechen ersetzt werden...

Das ist die Datei "Beispiel.dbs"

ausblenden Delphi-Quelltext
1:
2:
3:
4:
CELL(1;1).TEXT="Test;
CELL(2;1).TEXT="STRING A;
CELL(3;1).TEXT="STRING B;
CELL(3;2).TEXT="STRING C;


und das ist der Quellcode:

ausblenden volle Höhe 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:
28:
29:
30:
31:
  Tokens: Array [0 .. 8Of String = (
    '(',
    ')',
    ',',
    ';',
    '.',
    '=',
    '"',
    ';',
    ' '
  );

  liste: tstringlist;

  ...

  liste.loadfromfile(extractfilepath(application.exename) + 'beispiel.dbs');

  for i := 0 to liste.Count - 1 do // liste.count - 1 ist 3
  begin
    for ii := 0 to length(Tokens) - 1 do
    begin
      liste[i] := StringReplace(liste[i], Tokens[ii],
        #13#10 + Tokens[ii] + #13#10, [rfReplaceAll]);
    end;
  end;

  showmessage(inttostr(liste.Count - 1)); // liste.count - 1 ist faelschlicherweise immernoch 3

  Memo1.Text := liste.Text;
  showmessage(inttostr(Memo1.lines.Count - 1)); // nachdem ich stringlist in memo einfuege, aendert sich count zu 81


Wieso veraendert sich die Zeilenanzahl, wenn ich die StringList in ein Memo kopiere? Und warum aendert sich verdammt nochmal die Zeilenanzahl der StringList nicht, nachdem ich schon alles ersetzt habe?

Man braucht ja auch nicht die Liste zeilenweise ersetzen, sondern man kann auch folgendes aufrufen:

ausblenden Delphi-Quelltext
1:
2:
     liste.text := StringReplace(liste.text, Tokens[ii],
        #13#10 + Tokens[ii] + #13#10, [rfReplaceAll]);


Das funktioniert auch, aber waere bei 5000+ Zeilen viel zu langsam.
Perlsau
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mi 23.10.13 22:18 
Die erste Frage ist leicht zu beantworten: Die Zeilenzahl in einem Memo hängt von seiner Breite und dem Status des Property WordWrap ab. Steht WordWrap auf False, bleibt die Anzahl der Zeilen stets gleich. Steht dieses Property auf True, werden durch Zeilenumbrüche bei Breitenänderung quasi neue Zeilen erzeugt bzw. mehrere Zeilen zu einer zusammengefaßt.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 23.10.13 22:43 
Das dauert so viel zu lange... Immer wieder StringReplace, Schleifen, neue Strings, ...
Wie wäre es so?
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
function ReplaceTokens(const AValue: string): string;
var
  CurrentIndex: Integer;
  i: Integer;
begin
  SetLength(Result, Length(AValue) * 2);
  CurrentIndex := 0;
  for i := 1 to Length(AValue) do
  begin
    Inc(CurrentIndex);
    if CharInSet(AValue[i], ['('')'','';''.''=''"'';'' ']) then
    begin
      Result[CurrentIndex] := #13;
      Inc(CurrentIndex);
      Result[CurrentIndex] := #10;
    end
    else
      Result[CurrentIndex] := AValue[i];
  end;
  SetLength(Result, CurrentIndex);
end;
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Do 24.10.13 08:12 
Flexibler wäre es allerdings, wenn man die zu ersetzenden Zeichen als Parameter vom Typ TSysCharSet übergeben und dementsprechend auswerten würde.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 24.10.13 09:22 
Das geht ja 1:1 genauso, das ist ja nur das Drumherum, das habe ich einfach mal weggelassen...
ausblenden 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:
const
  cTokens: TSysCharSet = ['('')'','';''.''=''"'';'' '];

function ReplaceTokens(const AValue: stringconst ATokens: TSysCharSet): string;
var
  CurrentIndex: Integer;
  i: Integer;
begin
  SetLength(Result, Length(AValue) * 2);
  CurrentIndex := 0;
  for i := 1 to Length(AValue) do
  begin
    Inc(CurrentIndex);
    if CharInSet(AValue[i], ATokens) then
    begin
      Result[CurrentIndex] := #13;
      Inc(CurrentIndex);
      Result[CurrentIndex] := #10;
    end
    else
      Result[CurrentIndex] := AValue[i];
  end;
  SetLength(Result, CurrentIndex);
end;

// Verwendung
Liste.Text := ReplaceTokens(List.Text, cTokens);
florida Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 137

Windows 7 Home Premium, Windows XP Professional, Windows 2000
Delphi 2010 Architect
BeitragVerfasst: Do 24.10.13 11:36 
Frage scheint geklaert zu sein...

Danke an alle, vor allem an jaenicke, ich probier es heute mal so...

So kann ich es allerdings auch machen...

ausblenden 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:
28:
  Tokens: Array [0 .. 8Of String = (
    '(',
    ')',
    ',',
    ';',
    '.',
    '=',
    '"',
    ';',
    ' '
  );

  liste: tstringlist;

  ...

  liste.loadfromfile(extractfilepath(application.exename) + 'beispiel.dbs');

  for i := 0 to liste.Count - 1 do // liste.count - 1 ist 3
  begin
    for ii := 0 to length(Tokens) - 1 do
    begin
      liste[i] := StringReplace(liste[i], Tokens[ii],
        #13#10 + Tokens[ii] + #13#10, [rfReplaceAll]);
    end;
  end;

Liste.text := liste.text; // muss aufgerufen werden, damit die Zeilenanzahl veraendert wird!


Hier veraendert sich durch den letzten Aufruf die Zeilenanzahl auch und es geht wesentlich schneller, als wenn ich liste.text := stringreplace(...) schreibe.

Aber dein Code hoert sich auch nicht schlecht an, probier ich dann mal! :)
Blup
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 174
Erhaltene Danke: 43



BeitragVerfasst: Do 24.10.13 13:45 
user profile iconflorida hat folgendes geschrieben Zum zitierten Posting springen:
Wieso veraendert sich die Zeilenanzahl, wenn ich die StringList in ein Memo kopiere? Und warum aendert sich verdammt nochmal die Zeilenanzahl der StringList nicht, nachdem ich schon alles ersetzt habe?

Die Stringliste arbeitet schon richtig, nur nicht so wie du es erwartest.
Jeder String der Liste darf durchaus Zeilenumbrüche enthalten.

Vor dem Einlesen des Inhalts einer Liste kann man festlegen, wie der übergebene Text in einzelne Strings aufgeteilt wird.
Man kann sogar festlegen das CRLF gänzlich ignoriert wird und nur z.B. bei Semikolon getrennt wird:
ausblenden Delphi-Quelltext
1:
2:
3:
  Delimiter := ';';
  StrictDelimiter := True;
  DelimitedText := 'String0;Anfang String1' + #13#10 + 'Ende String1;String2';

Diese Aufteilung erfolgt aber nur beim Einlesen (z.B. Text oder DelimitedText zuweisen, ReadFromStream usw.).
Beim Auslesen wird das Ergebnis wieder nach dieser Vorschrift zusammengesetzt.
Nur deshalb ist ein schnelles Arbeiten mit den einzelnen Strings möglich.
Sollen neue Zeilen eingefügt werden, dann mit Insert.

Sollte eigentlich deutlich schneller gehn und auch keine Leerzeilen produzieren, ungetestet:
ausblenden volle Höhe 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:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
var
  boInsert: Boolean;
  i, iPos1, iPos2: Integer;

procedure DoInsert(const AValue: string);
begin
  if Length(AValue) = 0 then
    Exit;

   if boInsert then
   begin
     Inc(i);
     Liste.Insert(i, s)
   end
   else
   begin
     Liste[i] := s;
     boInsert := True;
   end;
end;

begin
  i := 0;
  while i < Liste.Count do
  begin
    sText := Liste[i];
    boInsert := False;
    iPos1 := 1;
    iPos2 := 1;
    while iPos2 < Length(sText) do
    begin
      if CharInSet(sText[iPos2], ATokens) then
      begin
        DoInsert(Copy(sText, iPos1, iPos2 - iPos1));
        DoInsert(sText[iPos2]);
        iPos1 := iPos2 + 1;
      end;
      Inc(iPos2);
    end;
    if boInsert then
      DoInsert(sText, iPos1, iPos2 - iPos1);
    Inc(i);
  end;
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 24.10.13 15:02 
Ich habe in meiner Variante absichtlich den String nur einmal verändert. Du veränderst jetzt mit dem Insert immer das ganze Array hinter der TStringList, das dürfte langsamer sein.

Das Auslesen als String und Setzen als String ist bei mir noch ein Overhead, aber das Auslesen aus der Datei muss man ja nicht als TStringList machen, es reicht ja z.B. ein TFileStream / TStringStream.
florida Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 137

Windows 7 Home Premium, Windows XP Professional, Windows 2000
Delphi 2010 Architect
BeitragVerfasst: Do 24.10.13 18:20 
Spaeter soll es ja ein Datenbanksystem werden...

Den Ausdruck CELL kann das Programm auch schon interpretieren und daraus eine Tabelle erstellen, allerdings bin ich auf die Idee gekommen, die Datenbank lieber in dbt (Tabellendatei) und dbs (Scriptdatei) zu unterteilen. Dann braeuchte man nicht alle Dateneintraege in den Script reinschreiben, sondern die Tabelle mit den Eintraegen wird nur gelesen und das Einlesen dauert ja nicht so lange als wenn man ein Haufen CELL-Ausdruecke verarbeiten muss.

Schoenen Abend noch :)
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 24.10.13 20:24 
Für den Zweck ist die Herangehensweise allerdings wenig geeignet...

Denn in einer Datenbank stehen unterschiedliche Datentypen. Deshalb sind Strings dafür eher keine so gute Repräsentation, zumindest nicht für alle Datentypen (Zahlen, Blobs, ...). Zudem dauert es ewig, wenn man für bestimmte Daten erst die ganze Datei vorher lesen muss, sprich trennzeichengetrennte Strings sind für eine echte Datenbank absolut ungeeignet. Dazu kommen dann noch Indizes für schnelle Zugriffe usw., damit man die richtigen Teile der Datenbank schnell findet, wenn man auf bestimmte Daten zugreifen möchte.
Ein wirklich performantes Datenbanksystem zu schreiben ist jedenfalls nicht einfach.

Warum möchtest du das Datenbanksystem denn selbst schreiben? Es gibt ja schließlich bereits sehr viele, an deren Qualität und Features weder du noch ich auch nur annähernd herankommen würden bei einem eigenen Programmierversuch.
florida Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 137

Windows 7 Home Premium, Windows XP Professional, Windows 2000
Delphi 2010 Architect
BeitragVerfasst: Sa 26.10.13 16:43 
Ich mach's eigentlich nur aus Lust. :)
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19314
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 26.10.13 19:42 
Das ist ein Argument. ;-)

Ich würde dann den Ansatz von DBase oder ähnlichen älteren Datenbanksystemen gehen. Dort gibt es eine feste Datensatzgröße, sprich es wird immer z.B. ein String auf die angegebene Länge gebracht und der Rest aufgefüllt. Auf die Weise kannst du schnell zu einem bestimmten Datensatz springen, da du die Position kennst. Und im Index (normalerweise eine zweite Datei) brauchst du zu bestimmten Werten nur die Positionsnummer kennen.
So kannst du einen Datensatz immer in einen festen Puffer kopieren und kommst an die Werte auch schnell heran.

Dann kannst du auch eine eigene Klasse von TDataSet ableiten um die Daten auch direkt z.B. als Datenquelle nutzen zu können.

Nebenbei:
Es gibt in den Beispielen zu Delphi auch eine fertige Implementierung für so etwas.
ausblenden Quelltext
1:
Samples\Delphi\Database\TextData