Autor Beitrag
Flamefire
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: 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.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 566
Erhaltene Danke: 10

Win 7, Ubuntu
Lazarus, Turbo Delphi, Delphu 7 PE
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2077
Erhaltene Danke: 2

Win XP
Delphi 5 Ent., Delphi 2007 Prof
BeitragVerfasst: Fr 03.07.09 13:47 
Vielleicht hilft dir TStringList.DelimitedText weiter?

_________________
PROGRAMMER: A device for converting coffee into software.
Niko S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 566
Erhaltene Danke: 10

Win 7, Ubuntu
Lazarus, Turbo Delphi, Delphu 7 PE
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 03.07.09 14:32 
Den Rest Umkopieren? Wozu? Das geht viel einfacher (und vor allem sehr viel schneller):
ausblenden 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); // CurrentText = 'Ein kleiner Text'
  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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 09.07.09 15:11 
Dann suche eben nach:
ausblenden Delphi-Quelltext
1:
#13#10 + DeineID + #9					
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 09.07.09 15:55 
user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
zelienumbruch +ID geht nicht weil dann das erste übersprungen wird
Was hindert sich daran die erste Zeile separat zu prüfen? :gruebel:
Flamefire Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 28

Win XP Home, Win XP Prof.
D7 Prof., D2006 Arch., BCB2006 Arch.
BeitragVerfasst: Do 09.07.09 16:38 
Flamefire Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: Do 09.07.09 17:44 
hab ich auch schon überlegt...aber wäre viel langsamer...wenn auch bequemer
alzaimar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2889
Erhaltene Danke: 13

W2000, XP
D6E, BDS2006A, DevExpress
BeitragVerfasst: 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:
ausblenden 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
ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 09.07.09 19:37 
user profile iconalzaimar hat folgendes geschrieben Zum zitierten Posting springen:
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:
ausblenden 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. user profile iconFlamefire hat das ja auch selbst bereits erkannt.

// EDIT:
user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1207
Erhaltene Danke: 31

Win 10
Delphi 2009 Pro, C++ (Visual Studio)
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 10.07.09 12:53 
user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
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. ;-)

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
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.