Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations wOOdy-Soft on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

find files or folders

Status
Not open for further replies.

jbpelletier

Programmer
Sep 8, 2001
232
CA
hi,

im looking for an equivalent of the "find files or folders" of Windows.

In fact, i need to find all *.somthing files located on a specific drive, then loop the list to process files returned by the search...

any help would be appriciate

jb
 
I think the FindFirst function in the file management routines is what you are looking for.

Leslie

Anything worth doing is a lot more difficult than it's worth - Unknown Induhvidual
 
Ahhh. You are about to do a recursive call to a procedure.
Be aware, this is not an easy task....

I've done a special FindFile procedure, and one of the argument is a procedure.
This procedure is called every time a file is found.

Code:
type
  TFindFileProc = procedure(const FoundPath, FoundFile: String) of Object;

procedure MultipleFileSearch(const PathName, FileName : string; const SubDirs: Boolean; OnFind: TFindFileProc);
var
  Rec: TSearchRec;
  Path: string;
begin
  Path := IncludeTrailingBackslash(PathName);
  if FindFirst(Path + FileName, faAnyFile - faDirectory, Rec) = 0 then
  try
    repeat
      OnFind(Path, Rec.Name);
    until FindNext(Rec) <> 0;
  finally
    FindClose(Rec);
  end;

  If not SubDirs then Exit;

  if FindFirst(Path + '*.*', faDirectory, Rec) = 0 then
  try
    repeat
      if ((Rec.Attr and faDirectory) <> 0) and (Rec.Name<>'.') and (Rec.Name<>'..') then
        MultipleFileSearch(Path + Rec.Name, FileName, True, OnFind);
    until FindNext(Rec) <> 0;
  finally
    FindClose(Rec);
  end;
end; //procedure FileSearch

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There is always another way to solve it, but I prefer my way.
 
Tanx,

i still have a little problem using somthing very similar to what Nordlund pasted

for exemple if i whant to find all *.ini file ill use

filename = *.ini

but the search will return me some non *.ini files that begin with .ini (exemple *.iniold)

i could probably add a condition in the code to avoid it.. but i whant to know if there is a parameter that do that already

tanx
jb
 
A few things:

1) The *.ini/*.iniold is a behavior of the OS (it treats *.ini as "*.ini*". You will have to post-process the name. Try going to a command window and typing "dir /s *.ini" from your root directory to see what I mean - it should return *.iniold as well if you have a file like that on your drive.

2) I have some nitpicks about Nordlund's code, but the biggest problem I see is that it appears there is no care in his/her filename pass for the nature of the LFNs. While the same commandline statement makes account of this, Delphi will not. To Delphi, "readme.txt" is different than "README.TXT" is different than "ReAdMe.TxT". Write/use an uppercase string function against the filename and your comparison string when you get it back if examples like the ones I typed are all the same to you.

3) Anything that can be written recursively can be written iteratively. This case is no exception. Beware of your stack space - you might run out and have a program crash!
 
I agree with Glenn, particularly point 3.
here's a non recursive example of using findfirst/findnext it loops until findnext runs out of files and returns non zero.

This would be even safer with a call to 'application.proccessmessages' inside the loop.

Code:
 FindFirst(First, faAnyFile, SearchRec);

   // keep looping until we run out of files store the file we find with the newest date
   // we should end up with the newset
   repeat // no then scan through the rest
       reslt := FindNext(SearchRec);
       T1 := filedatetodatetime(SearchRec.Time);
       IntNew := round(T1);
       IntLast := round(Last);
       if (reslt = 0) and (IntNew > IntLast) then
           begin
              Last := T1;
              Updatefile := UpdateLocation + Searchrec.name;
           end;
   until reslt <> 0;


Steve: Delphi a feersum engin indeed.
 
Hi there.
Thanks for the comments about the code snippet.

I wonder, how do you search for files in unknown numbers of subdirectories, if you doesn't use recursive calls?



~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There is always another way to solve it, but I prefer my way.
 
This isnt my code, but its supposed to add (an unknown number of) sudfolders to a treeview, it does work and its not recursive. Having got a list of subs you can then search for files.

Code:
procedure TNetBackup.addSubDirs( rootDir : string;  Node : TTreeNode; Tree: integer);
var
  SearchRec: TSearchRec;
  Result : integer;
  NewNode : TTreeNode;
begin    // find first file, Result is 0 if successful
    Result := FindFirst(rootDir + '*.*', faDirectory, SearchRec);
    while Result = 0 do
    begin
       if browser.isDirectory(SearchRec) then
         begin  // if directory is found add it as a child of prev node in TreeView
            if tree = 0 then
               NewNode := Folders.Items.AddChild(Node, SearchRec.Name)
            else
               NewNode := DestFolders.Items.AddChild(Node, SearchRec.Name);

            NewNode.SelectedIndex := 1;
            if (browser.hasSubDirectories( rootDir + SearchRec.Name ) ) then
               NewNode.HasChildren := true;
         end; // then continue searching }
         Result := FindNext(SearchRec);
    end;
    FindClose(SearchRec);
    Node.AlphaSort;
end;

Steve: Delphi a feersum engin indeed.
 
Hi.
What is Browser? 3rd party component?

I'm interested to replace my recursive find, because of the issues discussed above.

The drawback with this method is that you are required to use the TTreeView component.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There is always another way to solve it, but I prefer my way.
 
Nordlund No, no 3rd Party comps are used Browser is a another form in the project.

isdirectory looks like this

Code:
function TBrowser.IsDirectory( sr : TSearchRec ) : boolean;
// test if current file is a directory
begin
   isDirectory := ((sr.Attr and faDirectory > 0)
                  and
                  (sr.Name <> '.') and (sr.Name <> '..'));
end;

and hassubdirectorys (no recursion here either)
// Hmm not sure about that last line a bit too 'C'ish for me.

Code:
function TBrowser.hasSubDirectories( d : string ) : boolean;
// test in the directory indicated by the string 'd' contains
// subdirectories. If so, return true.
var
  SearchRec: TSearchRec;
  SResult : integer;
  noSubDirFound : boolean;
begin    // find first file, Result is 0 if successful
    noSubDirFound := true;
    SResult := FindFirst(d + '\*.*', faDirectory, SearchRec);
    while ((SResult = 0) and (noSubDirFound)) do
       begin
          if isDirectory(SearchRec) then noSubDirFound := false;
          SResult := FindNext(SearchRec);
       end;
    FindClose(SearchRec);
    hasSubDirectories := (noSubDirFound = false);
end;



Steve: Delphi a feersum engin indeed.
 
I wonder, how do you search for files in unknown numbers of subdirectories, if you doesn't use recursive calls?

Usually, you do either one of two things (that I know anyway) with normally recursive things to make them iterative:

1. Unroll the recursion if it occurs at the end of the code. A good example here is quicksort. You can remove the last recursion by making the IF condition a loop ("IF L < R THEN" becomes "WHILE L < R DO") and you set the new interval with an assign (L := i; if we take the right interval into the loop).

2. Use stacks to store the data you would recurse with when the recursion call normally comes and then just simply continue processing, and pop the values off the stack when your normal processing loop is done. This is how I have my normal findfile proc. You will notice a lot of memory being used in the stack sometimes, but this is normal, because it is a direct reflection of how much stack space you would have used in the recursion anyway.

Of course, as pointed out, there are advantages and disadvantages. Normally, recursive algorithms are easier to write (and in fact I would suggest to get it right using recursion before you try iteration), but the disadvantage there is as posted - they're unstable if the recursion use exceeds the stack space. Of course, the stack method has disadvantages too, but those can be mitigated a lot easier (dynamic memory allocation of the stacks) than running out of stack space can be mitigated.

Anyway, see what you think of something like this, which seems to work for me (of course, YMMV):

Code:
  const
    STACK_SIZE = 500;
  type
    mystack = array[1..STACK_SIZE] of TFileName;
  var
    drive: string;
    fileinput: string;
    found_count: longint;
    thestack: mystack;
    stackcount: longint;
    maxstack: longint;

 procedure listdirsizes(filedir: TFileName);
    { iterative.  Uses "thestack" to place directories to work on.
      then main proc pulls them off to work on }
    var
      f: TFileName;
      fileinfo, dirinfo: TSearchRec;
      retcode: longint;
    begin
      f := filedir + '\*.*';
      retcode := FindFirst(f, faAnyFile-faVolumeID, dirinfo);
      while retcode = 0 do
        begin
          { is this a directory listing? }
          if (dirinfo.attr and faDirectory = faDirectory) then
            begin
              if (dirinfo.name <> '.') and (dirinfo.name <> '..') then
                 begin
                   inc(stackcount);
                   if stackcount > maxstack then
                     maxstack := stackcount;
                   thestack[stackcount] := filedir + '\' + dirinfo.name;
                 end;
            end
          else
            { if not, is this my file I'm looking for? }
            if (upstr(dirinfo.name) = fileinput) then
              begin
                writeln('Found: ', filedir + '\' + dirinfo.name);
                inc(found_count, 1);
              end;
          retcode := FindNext(dirinfo);
        end;
      FindClose(dirinfo);
    end;

  procedure listdrive(filedir: TFileName);
    { main driver for file search - calls listdirsizes to do work
      for each directory }
    begin
      stackcount := 0;
      found_count := 0;
      listdirsizes(filedir);
      repeat
        filedir := thestack[stackcount];
        dec(stackcount);
        listdirsizes(filedir);
      until stackcount = 0;
      writeln;
      writeln(found_count, ' files found.');
      writeln('Maximum stack was: ', maxstack, ' out of ', STACK_SIZE,
              ' elements.');
    end;

Of course, if you do the dynamic memory allocation on the stack or are confident in your stack size, you can remove the maximum stack value processing.
 
Hehe... There is one way you can do to stop recursive operations to kill you machine...

...Do a counter, and prevent your function to call itself more than x times....

But this only prevent the crash... We shall not mention the function then... :)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There is always another way to solve it, but I prefer my way.
 
Hmm I am not convinced by this stack overflow stuff in a looping non recursive structure.

My code is only populating the treeview, its not a requirement, you could populate a stinglist.

The code snippits I posted were written by Huw Collingbourne, formally Delphi coloumist for PCPlus Magazine in the UK.




Steve: Delphi a feersum engin indeed.
 
Hmm I am not convinced by this stack overflow stuff in a looping non recursive structure."

No one was making an issue of your code that I see. That was merely pointing out that any recursively coded algorithm has potential issues with stack overflows since for each recursion a new copy of the code and local data are placed upon the stack.

Iteration eliminates this issue.
 
Glen9999, the compiler does not put a new copy of the code on the stack when a recursive routine is called. Only the local data is added to the stack.

It seems odd to me not to use a recursive routine when the solution to the problem is itself naturally recursive.

Recursive code is frequently much more concise and easier to understand than non recursive code for appropriate problems.

Compare this implementation of your listdrive procedure above:
Code:
var
  fileinput: string;

// A couple of functions to make the main code easier to read
  
function IsDirectory ( tsr: TSearchRec ): boolean;
begin
  result := ( ( tsr.attr and faDirectory ) = faDirectory ) and
            ( tsr.name <> '.' ) and
            ( tsr.Name <> '..' );
end;

function IsRequiredFile ( const filename: string ): boolean;
begin
  result := AnsiCompareFileName ( filename, fileinput ) = 0;
end;

procedure listdrive(filedir: TFileName);
var
  dirinfo: TSearchRec;
begin
  filedir := IncludeTrailingPathDelimiter(filedir);
  if FindFirst (filedir + '*.*', faAnyFile-faVolumeID, dirinfo) = 0 then begin
    repeat
      if IsDirectory(dirinfo) then
        listdrive(filedir + dirinfo.name)
      else if IsRequiredFile(dirinfo.Name) then
        writeln('Found: ', filedir + '\' + dirinfo.name);
    until FindNext(dirinfo) <> 0;
    FindClose(dirinfo);
  end;
end;
It is essentially the same code that you wrote but I got the compiler to do all the stack housekeeping. I would argue that is far less error prone and easier to understand and test than coding it yourself.

The main routine gets down to what you are trying to do:
Code:
Iterate through the files in the top level directory.  

If it is a directory then repeat the iteration process in that directory 
otherwise 
if we have the required file then write out the details.

There's little risk of a stack overflow. I think it is absurd to avoid recursive routines because there is a risk of a stack overflow if you make an error in writing the code. Things are likely to go wrong if you make an error in writing any code. A key to writing reliable and maintainable code is to make it as simple and clear as possible.

Andrew
Hampshire, UK
 
I Agree with Andrew about how the stack works, as for the reasons for using a particular looping structure, this is mainly down to your personal preference in my opinion.
At Uni they tried to teach us that programming should be done to fixed set of style rules (syntax is not the same thing), can't say I ever agreed with that, I would say it's part science part art form!





Steve: Delphi a feersum engin indeed.
 
There's little risk of a stack overflow. I think it is absurd to avoid recursive routines because there is a risk of a stack overflow if you make an error in writing the code."

Recursion is by necessity stack-intensive. And it doesn't take a coding error to see such a thing too - I've seen lots of stack overflows due to the stack simply running out during a recursive procedure or function. Now granted I researched it, and yes there's probably little chance of it in Delphi because it seems it has a much larger default for the stack than I'm used to seeing (who need 1MB for stack?!?) or is necessary. Seems to me like I need to add $M to my programs by default and lower that considerably.
 
Glenn9999,
Recursion is by necessity stack-intensive.
And your point is?

Local variables are stack based, period. It is immaterial whether the routine is recursive or not. So any routine that uses local variables is by necessity stack-intensive. So what?
Seems to me like I need to add $M to my programs by default and lower that considerably.

What's the point of lowering the size of your stack? Are you trying to get stack overflows?

Andrew
Hampshire, UK
 
So any routine that uses local variables is by necessity stack-intensive. So what?"

The parms (if non-"var") and the local variables get put onto the stack for each level of recursive call. So the size of the local variables gets multiplied by the maximum number of levels of recursion. Much more stack intensive than iteration.

"What's the point of lowering the size of your stack? Are you trying to get stack overflows?"

It's called making your program use resources efficiently. Why knowingly make a piece of bloatware that grabs 1MB for stack even for the simplest "Hello world" program?
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top