Link to home
Start Free TrialLog in
Avatar of shtern
shtern

asked on

Search in files

How can I search a string in the list of text files?

Thanks
ASKER CERTIFIED SOLUTION
Avatar of inthe
inthe

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of inthe
inthe

or you could use a tfinddialog fo text in a richedit:

procedure TForm1.Button1Click(Sender: TObject);
begin
FindDialog1.Execute;
end;

procedure TForm1.FindDialog1Find(Sender: TObject);
var
  FoundAt: LongInt;
begin
  with RichEdit1 do  begin
    FoundAt := Pos(FindDialog1.FindText, Text) - 1;
    if (FoundAt <> 0) then
    begin
      SetFocus;
      SelStart := FoundAt;
      SelLength := Length(FindDialog1.FindText);
      SelAttributes.Color:=clred;
      SelAttributes.style := [fsbold];
      clearselection;
    end;
  end;
end;
or you could use a tfinddialog fo text in a richedit:

procedure TForm1.Button1Click(Sender: TObject);
begin
FindDialog1.Execute;
end;

procedure TForm1.FindDialog1Find(Sender: TObject);
var
  FoundAt: LongInt;
begin
  with RichEdit1 do  begin
    FoundAt := Pos(FindDialog1.FindText, Text) - 1;
    if (FoundAt <> 0) then
    begin
      SetFocus;
      SelStart := FoundAt;
      SelLength := Length(FindDialog1.FindText);
      SelAttributes.Color:=clred;
      SelAttributes.style := [fsbold];
      clearselection;
    end;
  end;
end;
Avatar of shtern

ASKER

Hi, inthe,
sorry, I don't understand. As it stays in Delphi help, pos function finds just a substring in a string. Your second comment is nearer, but I need to search in  a lot of files (not in one which I can load in Richedit), e.g.  find files in a directory which contain my string, not in filename, but in contents.
ok ,
i had a quick look and found this,i havent time to test till tommorrow ,
but it will help you on your way for now,hope its more what you were after.

<start paste>

there are two tasks here: scanning the disk(s) for files with a set of extensions and scanning each file found for the phrase. In short, you want a Grep utility <g>. I'm sure you could find something on the usual Delphi websites ,dsp for instance:
http://sunsite.icm.edu.pl/delphi/
using GREP as search word.
 
Anyway, part 1 is a recursive FindFirst/FindNext loop. Here is a sample for that, which you will have to adapt to integrate the search for a phrase:
 
recursively scanning all drives
 
  { excerpt from form declaration, form has a listbox1 for the
    results, a label1 for progress, a button2 to start the scan,     an edit1 to get the search mask from, a button3 to stop
    the scan. }
  private
    { Private declarations }
    FScanAborted: Boolean;
 
  public
    { Public declarations }
    Function ScanDrive( root, filemask: String; hitlist: TStrings ): Boolean;
 
 
Function TForm1.ScanDrive( root, filemask: String; hitlist: TStrings ): Boolean;
  Function ScanDirectory( Var path: String ): Boolean;
    Var
      SRec: TSearchRec;
      pathlen: Integer;
      res: Integer;
    Begin
      label1.caption := path;
      pathlen:= Length(path);
      { first pass, files }
      res := FindFirst( path+filemask, faAnyfile, SRec );
      If res = 0 Then
      try
        While res = 0 Do Begin
          hitlist.Add( path + SRec.Name );
          res := FindNext(SRec);
        End;
      finally
        FindClose(SRec)
      end;
      Application.ProcessMessages;
      Result := not (FScanAborted or Application.Terminated);
      If not Result Then Exit;
 
      {second pass, directories}
      res := FindFirst( path+'*.*', faDirectory, SRec );
      If res = 0 Then
      try
        While (res = 0) and Result Do Begin
          If ((Srec.Attr and faDirectory) = faDirectory) and
            (Srec.name[1] <> '.')
          Then Begin
            path := path + SRec.name + '\';
            Result := ScanDirectory( path );
            SetLength( path, pathlen );
          End;
          res := FindNext(SRec);
        End;
      finally
        FindClose(SRec)
      end;
    End;
Begin
  FScanAborted := False;
  Screen.Cursor := crHourglass;
  try
    Result := ScanDirectory(root);
  finally
    Screen.Cursor := crDefault
  end;
End;
 
procedure TForm1.Button2Click(Sender: TObject);
Var
  ch: Char;
  root: String;
Begin
  root := 'C:\';
  For ch := 'A' to 'Z' Do Begin
    root[1] := ch;
    Case GetDriveType( Pchar( root )) Of
      DRIVE_FIXED, DRIVE_REMOTE:
        If not ScanDrive( root, edit1.text, listbox1.items ) Then           Break;
    End;
  End;
end;
 
procedure TForm1.Button3Click(Sender: TObject);
begin // aborts scan
  fScanAborted := True;
end;
 
And as a bonus here is a function to search a file for a string:
 
Function ScanFile( Const filename: String;
                    Const forString: String;
                    caseSensitive: Boolean ): LongInt;
{ returns position of string in file or -1, if not found }
Const
  BufferSize= $8001;  { 32K+1 bytes }
Var
  pBuf, pEnd, pScan, pPos : Pchar;
  filesize: LongInt;
  bytesRemaining: LongInt;
  bytesToRead: Word;
  F    : File;
  SearchFor: Pchar;
  oldMode: Word;
Begin
  Result := -1;  { assume failure }
  If (Length( forString ) = 0) or (Length( filename ) = 0) Then Exit;   SearchFor := Nil;
  pBuf      := Nil;
 
  { open file as binary, 1 byte recordsize }
  AssignFile( F, filename );
  oldMode := FileMode;
  FileMode := 0;    { read-only access }
  Reset( F, 1 );
  FileMode := oldMode;
  try { allocate memory for buffer and pchar search string }
    SearchFor := StrAlloc( Length( forString )+1 );
    StrPCopy( SearchFor, forString );
    If not caseSensitive Then  { convert to upper case }
      AnsiUpper( SearchFor );
    GetMem( pBuf, BufferSize );
    filesize := System.Filesize( F );
    bytesRemaining := filesize;
    pPos := Nil;
    While bytesRemaining > 0 Do Begin
      { calc how many bytes to read this round }
      If bytesRemaining >= BufferSize Then
        bytesToRead := Pred( BufferSize )
      Else
        bytesToRead := bytesRemaining;
 
      { read a buffer full and zero-terminate the buffer }
      BlockRead( F, pBuf^, bytesToRead, bytesToRead );
      pEnd := @pBuf[ bytesToRead ];
      pEnd^:= #0;
      { scan the buffer. Problem: buffer may contain #0 chars! So we         treat it as a concatenation of zero-terminated strings. }       pScan := pBuf;
      While pScan < pEnd Do Begin
        If not caseSensitive Then { convert to upper case }
          AnsiUpper( pScan );
        pPos := StrPos( pScan, SearchFor );  { search for substring }         If pPos <> Nil Then Begin { Found it! }
          Result := FileSize - bytesRemaining +
                    LongInt( pPos ) - LongInt( pBuf );
          Break;
        End;
        pScan := StrEnd( pScan );
        Inc( pScan );
      End;
      If pPos <> Nil Then Break;
      bytesRemaining := bytesRemaining - bytesToRead;
      If bytesRemaining > 0 Then Begin
      { no luck in this buffers load. We need to handle the case of         the search string spanning two chunks of file now. We simply         go back a bit in the file and read from there, thus inspecting         some characters twice
      }
        Seek( F, FilePos(F)-Length( forString ));
        bytesRemaining := bytesRemaining + Length( forString );       End;
    End; { While }
  finally
    CloseFile( F );
    If SearchFor <> Nil Then StrDispose( SearchFor );
    If pBuf <> Nil Then FreeMem( pBuf, BufferSize );
  end;
end; { ScanFile }
Sample bellow is shown how to find substring in list of texts files.

legend:
(TEdit)  Edit1.Text - string to search
(TListBox) ListBox1 - list of files to search
(TListBox) ListBox2 - result of search, if string found then filename + position of string in text file will there.

-----------------
function SearchInFile(aFileName,aSearchStr : string) : integer;
var L : TStringList;
begin
  result:=0;
  L:=TStringList.Create;
  try
    L.LoadFromFile(aFileName);
    result:=pos(aSearchStr,L.Text);
  finally
    L.Free;
  end;
end;

procedure TForm1.SpeedButton1Click(Sender: TObject);
var I,N : integer;
begin
   ListBox2.Clear; // clear result
   for I:=0 to ListBox1.Items.Count-1 do // iterate all files
   begin
     N:=SearchInFile(ListBox1.Items[I],Edit1.Text); // search
     if N > 0 // put result
        then ListBox2.Items.Add(ListBox1.Items[I]+' '+IntToStr(N-1));
   end;
end;
--------

Cheers,
Igor