Autor |
Beitrag |
florida
      
Beiträge: 137
Windows 7 Home Premium, Windows XP Professional, Windows 2000
Delphi 2010 Architect
|
Verfasst: 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"
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:
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 .. 8] Of String = ( '(', ')', ',', ';', '.', '=', '"', ';', ' ' );
liste: tstringlist;
...
liste.loadfromfile(extractfilepath(application.exename) + 'beispiel.dbs');
for i := 0 to liste.Count - 1 do 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)); Memo1.Text := liste.Text; showmessage(inttostr(Memo1.lines.Count - 1)); |
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:
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
|
Verfasst: 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
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 23.10.13 22:43
Das dauert so viel zu lange... Immer wieder StringReplace, Schleifen, neue Strings, ...
Wie wäre es so? 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
      
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: 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
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 24.10.13 09:22
Das geht ja 1:1 genauso, das ist ja nur das Drumherum, das habe ich einfach mal weggelassen... 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: string; const 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;
Liste.Text := ReplaceTokens(List.Text, cTokens); |
|
|
florida 
      
Beiträge: 137
Windows 7 Home Premium, Windows XP Professional, Windows 2000
Delphi 2010 Architect
|
Verfasst: 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...
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 .. 8] Of String = ( '(', ')', ',', ';', '.', '=', '"', ';', ' ' );
liste: tstringlist;
...
liste.loadfromfile(extractfilepath(application.exename) + 'beispiel.dbs');
for i := 0 to liste.Count - 1 do 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; |
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
      
Beiträge: 174
Erhaltene Danke: 43
|
Verfasst: Do 24.10.13 13:45
florida hat folgendes geschrieben : | 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:
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:
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
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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 
      
Beiträge: 137
Windows 7 Home Premium, Windows XP Professional, Windows 2000
Delphi 2010 Architect
|
Verfasst: 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
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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 
      
Beiträge: 137
Windows 7 Home Premium, Windows XP Professional, Windows 2000
Delphi 2010 Architect
|
Verfasst: Sa 26.10.13 16:43
Ich mach's eigentlich nur aus Lust. 
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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.
Quelltext 1:
| Samples\Delphi\Database\TextData |
|
|
|