shtern
asked on
Search in files
How can I search a string in the list of text files?
Thanks
Thanks
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
or you could use a tfinddialog fo text in a richedit:
procedure TForm1.Button1Click(Sender : TObject);
begin
FindDialog1.Execute;
end;
procedure TForm1.FindDialog1Find(Sen der: 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.FindTex t);
SelAttributes.Color:=clred ;
SelAttributes.style := [fsbold];
clearselection;
end;
end;
end;
procedure TForm1.Button1Click(Sender
begin
FindDialog1.Execute;
end;
procedure TForm1.FindDialog1Find(Sen
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.FindTex
SelAttributes.Color:=clred
SelAttributes.style := [fsbold];
clearselection;
end;
end;
end;
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.
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.ProcessMessage s;
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 }
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.ProcessMessage
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
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
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,aSe archStr : string) : integer;
var L : TStringList;
begin
result:=0;
L:=TStringList.Create;
try
L.LoadFromFile(aFileName);
result:=pos(aSearchStr,L.T ext);
finally
L.Free;
end;
end;
procedure TForm1.SpeedButton1Click(S ender: 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.I tems[I],Ed it1.Text); // search
if N > 0 // put result
then ListBox2.Items.Add(ListBox 1.Items[I] +' '+IntToStr(N-1));
end;
end;
--------
Cheers,
Igor
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,aSe
var L : TStringList;
begin
result:=0;
L:=TStringList.Create;
try
L.LoadFromFile(aFileName);
result:=pos(aSearchStr,L.T
finally
L.Free;
end;
end;
procedure TForm1.SpeedButton1Click(S
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.I
if N > 0 // put result
then ListBox2.Items.Add(ListBox
end;
end;
--------
Cheers,
Igor
procedure TForm1.Button1Click(Sender
begin
FindDialog1.Execute;
end;
procedure TForm1.FindDialog1Find(Sen
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.FindTex
SelAttributes.Color:=clred
SelAttributes.style := [fsbold];
clearselection;
end;
end;
end;