Autor Beitrag
galagher
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: 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:
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:
44:
45:
46:
47:
48:
49:
Buf: array [0..8192of Char;
//...
 try
  AssignFile(F, FName);
  FileMode := fmOpenRead;
   {$i-}
  Reset(F, 1);
   {$i+}

  LabeledEdit2Text := AnsiLowerCase(LabeledEdit2.Text);  //Der Suchtext

  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]) = #0then S[I] := ' '
    else                        S[I] := Buf[I];
   end;

   S := AnsiLowerCase(S);  //Beides (Suchtext und gelesener Dateiinhalt) in Kleinbuchstaben

     //Das Ganze vergleichen
   if Pos(LabeledEdit2Text, S) > 0 then  //Der Suchtext kommt mind. 1x vor
   begin
    Result := True;

    A_String := ReadBinFile(FName);  //Ganze Datei einlesen - A_String ist ein AnsiString
                                     //ReadBinFile wandelt dabei auch #0 in Leerzeichen um

      //Nun ermitteln, wie oft der Suchtext im String A_String vorkommt
    Found := CountPos(AnsiLowerCase(LabeledEdit2Text), AnsiLowerCase(A_String))

    break;  //Weiterlesen ist hier unnötig
   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):
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
{CountPos wurde im Original von Stefan Kellermann geschrieben und wurde nur angepasst}
function TForm1.CountPos(const subtext: AnsiString; Text: AnsiString): LongInt;
begin
 if (Length(subtext) = 0or (Length(Text) = 0or (Pos(subtext, Text) = 0then
  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:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
Buf: array [0..8192of Char;
//oder
Buf: array [0..1024of Char;
//oder sonstwas kann sich unter Umständen auf CountPos auswirken

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:
ausblenden 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]) = #0then 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6386
Erhaltene Danke: 146

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 800

win98, winXP
D4 Standard; D6 Personal
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: 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:
ausblenden Delphi-Quelltext
1:
2:
3:
Buf: array [0..8191of Char;  //dann klappt's
Buf: array [0..4096of Char;  //dann klappt's
Buf: array [0..8192of Char;  //dann klappt's nicht

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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: 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 :P)
ich schreib dir gleich mal funktion die das kann und denk ich mal nen bischen schneller ist :>
uall@ogc
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: Sa 05.02.05 14:07 
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:
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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 109



BeitragVerfasst: Sa 05.02.05 14:44 
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
   for I := 1 to Length(Buf) do
   begin
    if (Char(Buf[I]) = #0then 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 109



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 109



BeitragVerfasst: 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:

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 2510
Erhaltene Danke: 44

Windows 10 Home
Delphi 10.1 Starter, Lazarus 2.0.6
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 5284
Erhaltene Danke: 27

Win Vista (32), Win 7 (64)
Eclipse, SciTE, Lazarus
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: Sa 05.02.05 15:28 
hab einen fehler im quelltext gefunden -> (b^ < ord('Z')) muss zu (b^ <= ord('Z')) gemacht werden