Autor |
Beitrag |
galagher
Beiträge: 2522
Erhaltene Danke: 44
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Fr 04.02.05 21:18
Hallo!
Ich bastle an einem Datei-Suchprogramm, das auch Text in Dateien finden kann und die Anzahl der Vorkommen des gesuchten Textes ausgibt. Dazu lese ich die Datei mit BlockRead ein:
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: 44: 45: 46: 47: 48: 49:
| Buf: array [0..8192] of Char; try AssignFile(F, FName); FileMode := fmOpenRead; {$i-} Reset(F, 1); {$i+}
LabeledEdit2Text := AnsiLowerCase(LabeledEdit2.Text); Result := False; S := '';
repeat Buf := ''; BlockRead(F, Buf, SizeOf(Buf), NumRead);
SetLength(S, Length(Buf)); for I := 1 to Length(Buf) do begin if (Char(Buf[I]) = #0) then S[I] := ' ' else S[I] := Buf[I]; end;
S := AnsiLowerCase(S); if Pos(LabeledEdit2Text, S) > 0 then begin Result := True;
A_String := ReadBinFile(FName); Found := CountPos(AnsiLowerCase(LabeledEdit2Text), AnsiLowerCase(A_String))
break; end;
until (NumRead = 0);
CloseFile(F);
except ShowMessage('Zugriff verweigert: '+FName); Result := True; end; |
Hier die Funktion CountPos, die ermittelt, wie oft ein String in einem anderen enthalten ist. (StrReplace tut das selbe wie StringReplace, nur mit AnsiString und LongInt statt String und Integer):
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| function TForm1.CountPos(const subtext: AnsiString; Text: AnsiString): LongInt; begin if (Length(subtext) = 0) or (Length(Text) = 0) or (Pos(subtext, Text) = 0) then Result := 0 else Result := (Length(Text) - Length(StrReplace(Text, subtext, '', [rfReplaceAll]))) div Length(subtext); end; |
Nun tritt das Phänomen auf, dass CountPos fast immer die korrekte Anzahl ermittelt, manchmal, bei mehreren hundert Vorkommen des Suchtextes jedoch um 1 bis 2 Stellen weniger errechnet als mit einem anderen Datei-Suchprogramm (von Norton, also zuverlässig!). Offenbar ist es auch nicht egal, mit welchem Wert man das Array Buf angibt:
Delphi-Quelltext 1: 2: 3: 4:
| Buf: array [0..8192] of Char; Buf: array [0..1024] of Char; |
Meine Fragen nun: Ist BlockRead wirklich ungenau, oder wie kann man auf bessere und schnellere Weise ermitteln, wie oft ein String in einem anderen enthalten ist?
Wo könnte der Fehler liegen? Für Hilfe wäre ich jedenfalls sehr dankbar!
//Edit: Nur der Ordnung halber:
Hier der richtige Code - bei Buf muss -1 angegeben werden, da es mit 0 beginnt, während S als String mit 1 beginnt:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| S := '';
repeat Buf := '';
BlockRead(SourceFile, Buf, Sizeof(Buf), NumRead); SetLength(S, NumRead);
for I := 1 to NumRead do begin if (Char(Buf[I-1]) = #0) then S[I] := ' ' else S[I] := Buf[I-1]; end;
AString := AString+S;
until (NumRead = 0); |
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
Zuletzt bearbeitet von galagher am So 06.02.05 18:13, insgesamt 3-mal bearbeitet
|
|
jasocul
Beiträge: 6388
Erhaltene Danke: 146
Windows 7 + Windows 10
Sydney Prof + CE
|
Verfasst: Fr 04.02.05 21:59
Vielleicht habe ich es überlesen, aber bedenkst du den Sonderfall, wenn der Suchbegriff da ist, aber gerade über die Buffer-Grenze geht? Wenn also nur ein Teil des Strings im Buffer ist?
|
|
galagher
Beiträge: 2522
Erhaltene Danke: 44
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Fr 04.02.05 22:17
jasocul hat folgendes geschrieben: | Vielleicht habe ich es überlesen, aber bedenkst du den Sonderfall, wenn der Suchbegriff da ist, aber gerade über die Buffer-Grenze geht? Wenn also nur ein Teil des Strings im Buffer ist? |
Zuerst mal danke für die Antwort!
Wenn das wirklich so ist, was kann ich da machen? Ich denke doch, BlockRead liest in den Buffer, und zwar maximal SizeOf(Buffer), oder, wenn der noch ungelesene Teil kleiner ist, dann eben weniger. Oder nicht? Denn ich habe herausgefunden, dass der Suchbegiff tatsächlich da ist, nur eben nicht "entdeckt" wird!
Kennt jemand eine Lösung?
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
|
|
grayfox
Beiträge: 800
win98, winXP
D4 Standard; D6 Personal
|
Verfasst: Fr 04.02.05 23:24
hallo galagher!
ich versuche dir mit einem beispiel zu verdeutlichen, was dir jacosul schon mit worten zu erklären versucht hat:
das auslesen einer datei mittels blockread ist für deinen anwendungsfall nicht geeignet, da folgenes auftreten kann
beispiel: du suchst in deinem text nach 'Abendessen'
- du hat eine blockgröße von zb 8096 bytes definiert - somit werden 8096 bytes in den buffer gelesen
- in deinem buffer steht nun ' xxxxxxxx Abende' und dann sind die 8096 bytes voll
- somit ist das wort 'Abendessen' nicht vollständig und wird auch nicht als solches erkannt
- der nächste block beginnt mit 'ssen xxxxxxxxxxx' - dort wird der suchbegriff daher auch nicht gefunden.
verstanden? na hoffentlich!
abhilfe schafft bereits ein zeilenweises auslesen der textdatei mit 'readln', oder wenn die texte nicht zu lang sind, ein einlesen in eine stringlist.
somit bleiben schon mal die wörter zusammen und werden nicht abgeschnitten.
probleme kann es natürlich noch geben, wenn die wörter am zeilenende abgeteilt sind...
_________________ mfg, stefan
|
|
uall@ogc
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Fr 04.02.05 23:26
1. wenn du ein array machst bitte in 2er potenzen also von 0..8192-1
2. kann es sein das der string zerschnitten wird (1. block 'hehehehal' 2. block 'lohehehehe' wenn z.b. nach hallo suchst (wie jasocul gesagt)
3. würde cih dir tfilestream empfehlen, oder wenns mit blockread machen willst musst du mittels
seek an die jetzige position- länge des textes der gesucht werden soll springen, dann erst nächsten block und das wiederholen
text = 'aaaa'
x: integer = 1;
leseblock (8192 bytes) <-|
inc(x); |
springe mit seek an stelle (x*8192-length(text)) -|
das musst du bei tfilstream auch machen aber da brauchste nicht so auf excpetions achten :>
würde ich mir ne eigene pos funktion schreiben und alles im array of byte machen, das was eingelesen wird sowie der string der geuscht werden soll
|
|
galagher
Beiträge: 2522
Erhaltene Danke: 44
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Sa 05.02.05 13:48
grayfox hat folgendes geschrieben: | - du hat eine blockgröße von zb 8096 bytes definiert - somit werden 8096 bytes in den buffer gelesen
- in deinem buffer steht nun ' xxxxxxxx Abende' und dann sind die 8096 bytes voll
- somit ist das wort 'Abendessen' nicht vollständig und wird auch nicht als solches erkannt |
Danke, klar, so kann das nicht immer klappen! Aber ich lese die Datei als Ganzes in eine AnsiString-Variable ein und suche erst dann im gesamten String (=Dateiinhalt). Dazu habe ich mir (mit Hilfe aus dem Internet und durch Lesen im Delphi-Forum) eine Prozedur ReadFromBinFile geschrieben, die mir #0 in Leerzeichen umwandelt. Das klappt sogar sehr schnell.
Was ich aber nicht kapiere:
Delphi-Quelltext 1: 2: 3:
| Buf: array [0..8191] of Char; Buf: array [0..4096] of Char; Buf: array [0..8192] of Char; |
Ich lese IMMER den GANZEN Dateiinhalt und suche erst dann. Es kalppt auch, wenn ich in ein RichEdit einlese und dann in RichEdit1.Text suche. Seltsam, oder?
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
|
|
uall@ogc
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Sa 05.02.05 13:50
es sollte bei allen 3 klappen aber nimm halt besser ne 2er potzen, (also gewöhn dir das an )
ich schreib dir gleich mal funktion die das kann und denk ich mal nen bischen schneller ist :>
|
|
uall@ogc
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Sa 05.02.05 14:07
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: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62:
| function findinfile(fn,text: string): boolean; procedure lowercasep(p: pointer; size: integer); var i: integer; b: ^byte; begin for i := 1 to size do begin b := pointer(integer(p)+i-1); if ((b^ >= ord('A')) and (b^ <= ord('Z'))) then b^ := b^-(ord('A') - ord('a')); end; end;
function searchin(p: pointer; text: string; bsize: integer): boolean; var i,j,k: integer; b: ^byte; begin result := false; text := lowercase(text); lowercasep(p,bsize); for i := 1 to bsize-length(text) do begin k := 0; for j := 1 to length(text) do begin b := pointer(integer(p)+i+j-2); if b^ = byte(text[j]) then inc(k); end; if (k = length(text)) then begin result := true; exit; end; end; end;
const bufsize = 4096; var p: pointer; fm: tfilestream; sf, read: integer; begin result := false; if fileexists(fn) then begin getmem(p,bufsize); fm := tfilestream.create(fn,fmopenread); sf := fm.Size; while sf > 0 do begin read := fm.Read(p^,bufsize); if searchin(p,text,read) then begin result := true; exit; end; sf := sf-bufsize-length(text); fm.Seek(-length(text),1) end; fm.free; freemem(p); end; end; |
ungetestet, hab ich blind geschrieben
Zuletzt bearbeitet von uall@ogc am Sa 05.02.05 15:28, insgesamt 1-mal bearbeitet
|
|
I.MacLeod
Beiträge: 109
|
Verfasst: Sa 05.02.05 14:44
Delphi-Quelltext 1: 2: 3: 4: 5:
| for I := 1 to Length(Buf) do begin if (Char(Buf[I]) = #0) then S[I] := ' ' else S[I] := Buf[I]; end; |
imho müsste da statt Length(Buf) NumRead stehen.
_________________ {$APPTYPE CONSOLE}uses SysUtils;const a='{$APPTYPE CONSOLE}uses SysUtils;const a=%s;begin write(Format(a,[#39+a+#39]))end.';begin write(Format(a,[#39+a+#39]))end.
|
|
uall@ogc
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Sa 05.02.05 14:52
das ist zu vernachlässigen (wäre zwar bisl schneller aber egal) da length(buf) immer >= ist wie numread
|
|
I.MacLeod
Beiträge: 109
|
Verfasst: Sa 05.02.05 14:57
Wirklich? Wenn die Datei 12000 Bytes groß ist, ist Numread beim ersten Mal 8192 und beim zweiten Mal 3808. Wenn aber z.B. an Byte 4000 der ersten Runde der Suchstring vorkam, wird der so doch jetzt nochmal mit eingelesen, oder nicht? (Zusätzlich zu dem was oben schon genannt wurde)
...und die Länge vom String müsste auch auf Numread gesetzt werden.
_________________ {$APPTYPE CONSOLE}uses SysUtils;const a='{$APPTYPE CONSOLE}uses SysUtils;const a=%s;begin write(Format(a,[#39+a+#39]))end.';begin write(Format(a,[#39+a+#39]))end.
|
|
uall@ogc
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Sa 05.02.05 15:00
1. es wird nur geschaut ob der string da ist, wenn der gefunden wurde dann ist sofort ende
2. blöd isses nur bei dateien die < 8192 sind weil dann ist der speicher eventl nicht initialisert
3. funzt die methode ja glaub ich nicht
4. hab ich ja eine neue geschrieben die gehen sollte
|
|
galagher
Beiträge: 2522
Erhaltene Danke: 44
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Sa 05.02.05 15:01
uall@ogc hat folgendes geschrieben: | es sollte bei allen 3 klappen aber nimm halt besser ne 2er potzen |
Kannst du mir ein praktisches Beispiel dazu geben, wie ich das mache?
uall@ogc hat folgendes geschrieben: | ich schreib dir gleich mal funktion die das kann und denk ich mal nen bischen schneller ist :> |
Ich vertehe es nicht wirklich und es will mir auch nicht gelingen, den Zähler einzubauen, der die Anzahl der Vorkommen des Suchtextes zählt! Der Code ist korrekt, findet aber auch Dateien ohne den Suchtext!
Aber das mit den 2er Potenzen - kannst du mir da ein Beispiel geben? Danke!
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
|
|
uall@ogc
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Sa 05.02.05 15:05
das mit den zweierpotenzen ist nur ne schönheitssache
z.b. var bub: array[0..8192-1] of byte und nicht array[0..8192] da du dann 8193 bytes hättest
2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192
sorry ich weiß nicht was an deinem code falsch ist aber vill kommst ja mit meinem code weiter...
|
|
galagher
Beiträge: 2522
Erhaltene Danke: 44
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Sa 05.02.05 15:09
uall@ogc hat folgendes geschrieben: | 3. funzt die methode ja glaub ich nicht |
Es geht, wenn man:
1. Entweder mit einer Textkomponente (Memo etc.) arbeitet und dann in zB. Memo1.Text sucht (ist aber wenig professionell, zumal man das Memo nur dazu braucht, nicht, um Text darzustellen).
2. Wenn man etwa "Buf: array [0..8191] of Char;" angibt - in diesem Zusammenhang verstehe ich das mit den 2er-Potenzen nicht!
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
|
|
I.MacLeod
Beiträge: 109
|
Verfasst: Sa 05.02.05 15:12
Also...
1. Du willst die Anzahl der Vorkommen eines Suchstrings in einer Datei ermitteln
2. ReadBinFile liest die ganze Datei ein
3. CountPos gibt an, wie oft der erste Parameter im zweiten Vorkommt.
Stimmt das alles? Dann frag ich mich, warum du es nicht einfach so machst:
Delphi-Quelltext 1:
| Found := CountPos(AnsiLowerCase(LabeledEdit2Text), AnsiLowerCase(ReadBinFile(FName))) |
Du liest ja so oder so die gesamte Datei ein. Hiernach müsste jedenfalls in Found die Anzahl drinstehen.
_________________ {$APPTYPE CONSOLE}uses SysUtils;const a='{$APPTYPE CONSOLE}uses SysUtils;const a=%s;begin write(Format(a,[#39+a+#39]))end.';begin write(Format(a,[#39+a+#39]))end.
|
|
uall@ogc
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Sa 05.02.05 15:15
1. vergiss das mit der 2er potenz einfach
2. memo kannste kniggn (wegen #0)
3. große dateien ganz einlesen ist auch fürn *popo*
|
|
galagher
Beiträge: 2522
Erhaltene Danke: 44
Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
|
Verfasst: Sa 05.02.05 15:16
uall@ogc hat folgendes geschrieben: | z.b. var bub: array[0..8192-1] of byte und nicht array[0..8192] da du dann 8193 bytes hättest |
Da kann ich ja gleich 8191 angeben, denn nach Adam Riese: 8192-1 = 8191! Jedenfalls: mit 8192-1 klappt es. Ich habe auch NumRead anstelle von Length(Buf) angegeben - je schneller, desto besser!
Dein Code funktioniert - hab' ihn vorher nur falsch eingesetzt! Hut ab - du zauberst sowas in 17 Minuten aus dem Hut, testest es nicht mal und es haut hin! Danke!
_________________ gedunstig war's - und fahle wornen zerschellten karsig im gestrock. oh graus, es gloomt der jabberwock - und die graisligen gulpen nurmen!
|
|
F34r0fTh3D4rk
Beiträge: 5284
Erhaltene Danke: 27
Win Vista (32), Win 7 (64)
Eclipse, SciTE, Lazarus
|
Verfasst: Sa 05.02.05 15:16
meinst du dass #0 net geht ? bei mir ist das bei der listbox so, alles nach einem #0 wird nicht mehr angezeigt
|
|
uall@ogc
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Sa 05.02.05 15:28
hab einen fehler im quelltext gefunden -> (b^ < ord('Z')) muss zu (b^ <= ord('Z')) gemacht werden
|
|
|