| Autor |
Beitrag |
Flamefire
      
Beiträge: 1207
Erhaltene Danke: 31
Win 10
Delphi 2009 Pro, C++ (Visual Studio)
|
Verfasst: Fr 03.07.09 13:30
Hallo,
ich habe eine Datei im Unicode-Format vorliegen, deren Daten Tab-separiert sind.
Also z.b.
| Zitat: | ID1 Data1 Data2 Nummer1 Nummer2
ID2 Data1 Data2 Nummer1 Nummer2
ID3 Data1 Data2 Nummer1 Nummer2
ID4 Data1 Data2 Nummer1 Nummer2 |
wie kann ich so eine Datei schnell laden, parsen und durchsuchen?
Es geht mir darum, dass ich eine ID habe und die dazugehörigen Daten über diese Einlesen muss.
In c# gibt es ja für sowas z.b. linq statements (auch wenn ich nicht 100% weiß was das ist, sieht nur nach ner SQL-Ähnlichen abfrage eines textes aus)
|
|
Niko S.
      
Beiträge: 566
Erhaltene Danke: 10
Win 7, Ubuntu
Lazarus, Turbo Delphi, Delphu 7 PE
|
Verfasst: Fr 03.07.09 13:34
Ich hab das damals mit Pos und Copy gemacht.
Mit Pos kannst du die Position vom tab herausfinden.
Mit Copy kannst du etwas rauskopieren.
Ich hab das so gemacht dass ich immer das erste vor dem Tab kopiert habe und dann alles hinter dem Tab,
wieder in die selbe zeile kopiert habe, um wieder an das erste ran zu kommen.
Gibt bestimmt noch ne schnellere Methode ..
|
|
Flamefire 
      
Beiträge: 1207
Erhaltene Danke: 31
Win 10
Delphi 2009 Pro, C++ (Visual Studio)
|
Verfasst: Fr 03.07.09 13:42
das war auch mein erster ansatz...
aber scheint mir zu langsam zu sein
weil die datei ist n paar kb groß und da jedesmal was umzukopieren...
|
|
Xentar
      
Beiträge: 2077
Erhaltene Danke: 2
Win XP
Delphi 5 Ent., Delphi 2007 Prof
|
Verfasst: Fr 03.07.09 13:47
Vielleicht hilft dir TStringList.DelimitedText weiter?
_________________ PROGRAMMER: A device for converting coffee into software.
|
|
Niko S.
      
Beiträge: 566
Erhaltene Danke: 10
Win 7, Ubuntu
Lazarus, Turbo Delphi, Delphu 7 PE
|
Verfasst: Fr 03.07.09 14:20
Ich hab das schon öfter so mit der Methode gemacht und ich muss sagen..
Selbst bei über 4000 einträgen kann man das ding in mehreren ms laden.
Also so langsam ist die Methode nicht!
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Fr 03.07.09 14:32
Den Rest Umkopieren? Wozu? Das geht viel einfacher (und vor allem sehr viel schneller): Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| var MyString: String; CurrentText: PChar; SpacePos: Integer; begin MyString := 'Ein kleiner Text';
CurrentText := PChar(MyString); SpacePos := Pos(' ', CurrentText); ShowMessage(Copy(CurrentText, 1, Pred(SpacePos))); Inc(CurrentText, SpacePos); | Und so weiter. Man schiebt also nur den Startzeiger weiter statt da was zu kopieren. Das lässt sich natürlich noch beliebig optimieren für den speziellen Zweck.
Auf ähnliche Weise lade ich eine 20 MiB .reg Registrydatei mit 320.000 Zeilen in unter einer halben Sekunde. 
|
|
Flamefire 
      
Beiträge: 1207
Erhaltene Danke: 31
Win 10
Delphi 2009 Pro, C++ (Visual Studio)
|
Verfasst: Do 09.07.09 15:00
hmm...wie lässt sich das noch optimieren?
das mit dem zeiger ist schon mal gut, da ich die datei als buffer in nem dynamischen array habe.
ich will jetzt in der datei den 2., 5. und 7. eintrag zu einer ID haben
die ID ist ein string (entweder eine zahl als string oder direkt ein text wie "Object111")
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 09.07.09 15:06
Naja, zuerst nach der ID plus dem Tab suchen, der Einfachheit halber am besten einfach mit Pos, dann den Zeiger dorthin schieben. So, und jetzt kannst du mit dem PChar als Zeiger direkt arbeiten. Jetzt suchst du nach dem nächsten Tab und packst dessen Position in eine Variable. Wenn es einer der gesuchten Werte ist, kopierst du den heraus. Dann verschiebst du den Zeiger um diesen Wert. Und so weiter. 
|
|
Flamefire 
      
Beiträge: 1207
Erhaltene Danke: 31
Win 10
Delphi 2009 Pro, C++ (Visual Studio)
|
Verfasst: Do 09.07.09 15:09
wird nicht ganz gehen:
Bsp: | Zitat: |
2 c d 33 1
11 c d 33 1
1 a b 22 1
|
wenn ich jz nach "1" suche findet er u.U. nicht meine ID sondern einen wert oder einen teil einer ID
ich müsste also immer den ersten teil bis zum tab kopieren, vergleichen und beim miss bis zum zeilenumbruch verschieben...
geht es effektiver?
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 09.07.09 15:11
Dann suche eben nach: Delphi-Quelltext Also Zeilenumbruch plus ID plus Tab. Das sollte doch wohl eindeutig sein.
// EDIT:
Wobei bei deinem Beispiel ja auch das genannte ID + Tab eindeutig wäre. 
|
|
Flamefire 
      
Beiträge: 1207
Erhaltene Danke: 31
Win 10
Delphi 2009 Pro, C++ (Visual Studio)
|
Verfasst: Do 09.07.09 15:51
zelienumbruch +ID geht nicht weil dann das erste übersprungen wird
ID+Tab geht nicht weil er bei "1" dann die "1#9" und "11#9" und "21#9" usw findet
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 09.07.09 15:55
|
|
Flamefire 
      
Beiträge: 1207
Erhaltene Danke: 31
Win 10
Delphi 2009 Pro, C++ (Visual Studio)
|
Verfasst: Do 09.07.09 16:32
ok stimmt...
ich glaube sogar am anfang 2 bytes übrig zu haben...da hau ich den zeilenumbruch rein und fertig
ansonste fände ich es unschön eine zeile und dann in ner schleife den rest prüfen zu müssen...
obwohl...nja mal gucken.
|
|
R4id
      
Beiträge: 28
Win XP Home, Win XP Prof.
D7 Prof., D2006 Arch., BCB2006 Arch.
|
Verfasst: Do 09.07.09 16:38
|
|
Flamefire 
      
Beiträge: 1207
Erhaltene Danke: 31
Win 10
Delphi 2009 Pro, C++ (Visual Studio)
|
Verfasst: Do 09.07.09 17:44
hab ich auch schon überlegt...aber wäre viel langsamer...wenn auch bequemer
|
|
alzaimar
      
Beiträge: 2889
Erhaltene Danke: 13
W2000, XP
D6E, BDS2006A, DevExpress
|
Verfasst: Do 09.07.09 18:59
Wie wäre es mit einer fertigen Komponente?
www.delphipraxis.net...highlight=tcsvreader
Vielleicht etwas Overkill, ist aber recht flott.
P.S.: 'Pos' ist nicht gerade das Schnellste, wenn es darum geht, das nächste Zeichen zu finden.
Alleine das hier ist um den Faktor 40-50 schneller:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| Function CharPos(c: Char; p: Pchar): Integer; var q: PChar;
begin q := p; while (p^ <> #0) and (p^<>c) do inc(p); if p^ = #0 then Result := -1 else Result := integer(p) - integer(q) + 1; end; |
Wohingegen das hier nochmal um 25% schneller ist
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| Function FasterCharPos (c: Char; const s : String): Integer; Begin For Result := 1 to Length(s) do if s[Result]=c then Exit; Result := -1; End; |
obwohl es mit Strings und Index arbeitet, anstatt mit Pointern. 
_________________ Na denn, dann. Bis dann, denn.
|
|
Flamefire 
      
Beiträge: 1207
Erhaltene Danke: 31
Win 10
Delphi 2009 Pro, C++ (Visual Studio)
|
Verfasst: Do 09.07.09 19:30
die kompo bringt nix...
muss nur jeweils ein datum lesen
warum sollte ich also die ganze datei parsen
das das 2.Schneller ist kann ich mir nur durch den vergleich mit #0 und die while schleife erklären
for schleifen werden besser optimiert und er hat einen vergleich je schleifendurchlauf weniger
werd also beides kombinieren um eine zeile zu parsen
müsste trotzdem noch die date nach nem string durchsuchen
wie wäre es mit:
setze Pointer an zeilenanfang(wo ID steht)
mach nen CompareMem darauf
wenn falsch suche mit fastercharpos nach zeilenende und setze pointer um
und wieder von vorne das ganze.
sollte schneller sein, oder?
PS: Optimierungen bringen was da es um eine 10MB unicode datei geht, und die zugriffe (suchen) sehr häufig sind.
mach zwar nen cache rein, aber trotzdem
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 09.07.09 19:37
alzaimar hat folgendes geschrieben : | obwohl es mit Strings und Index arbeitet, anstatt mit Pointern.  |
Naja, das mit Pointern ist ja auch nicht auf Geschwindigkeit geschrieben, so ist es natürlich langsamer...
So ist es ca. 20-25% schneller als deine zweite Methode: Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| function FasterCharPos2(c: Char; const s: String): Integer; var CurChar: PChar; Begin CurChar := Pointer(s); for Result := 1 to Length(s) do if CurChar^ = c then Exit else Inc(CurChar); Result := -1; end; | Der Grund dafür ist, dass die while-Schleife natürlich langsamer ist, wenn man jedesmal auf das Nullzeichen testet statt einer optimierbaren for-Schleife. Flamefire hat das ja auch selbst bereits erkannt.
// EDIT:
Flamefire hat folgendes geschrieben : | PS: Optimierungen bringen was da es um eine 10MB unicode datei geht, und die zugriffe (suchen) sehr häufig sind.
mach zwar nen cache rein, aber trotzdem |
Du kannst dir ja die Unit FileAccessUtils.pas aus meinem Registryeditor anschauen. Da nutze ich MMFs zum Zwischenspeichern und eine 350 MiB .reg Datei dauert nur wenige Sekunden (ca. 9) und ist dann komplett analysiert und in den Baum eingelesen. 
|
|
Flamefire 
      
Beiträge: 1207
Erhaltene Danke: 31
Win 10
Delphi 2009 Pro, C++ (Visual Studio)
|
Verfasst: Fr 10.07.09 10:11
deine mehtode der charpos fkt ist echt gut
werde ich nehmen
und 9 sekunden sind für mich 9 sekunden zuviel
ich habe praktisch 30 dateien noch o.g. schema und will sehr häufig aus einer der dateien ein datum lesen
ich optimier zwar das programm dass es seltener etwas lesen muss aber da ich sonst 300mb mehr speicher brauche, lasse ich nicht alle daten im speicher sonder lade sie bei benutzung aus der datei
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Fr 10.07.09 12:53
Flamefire hat folgendes geschrieben : | | und 9 sekunden sind für mich 9 sekunden zuviel |
Wobei ich in der Zeit die kompletten 350 MiB lade, d.h. da spielt die Festplattengeschwindigkeit auch schon eine Rolle, da das knapp 40 MiB/s sind.
Flamefire hat folgendes geschrieben : | | ich optimier zwar das programm dass es seltener etwas lesen muss aber da ich sonst 300mb mehr speicher brauche, lasse ich nicht alle daten im speicher sonder lade sie bei benutzung aus der datei |
Da könnte vielleicht auch die Methode gut sein, die ich beim Registryeditor nutze:
Ich lade die Datei indem ich die Registryschlüssel auslese und die Werte ignoriere. Ich merke mir aber die Position. Wenn ich dann die Werte zu einem Schlüssel brauche mache ich eine MMF an der Stelle auf und lese die Werte aus. Das Setzen der Position und das Auslesen dauert so nur Millisekunden, egal wo in der Datei das dann ist.
Ich weiß ja nicht wie das bei dir genau aussieht, aber vielleicht kannst du von den Dateien (evtl. sogar schon im Hintergrund) auch so ein Inhaltsverzeichnis laden. Dann könntest du danach die Werte an den Positionen extrem schnell laden.
|
|