【问题标题】:How to Search a File through all the SubDirectories in DelphiDelphi中如何通过所有子目录搜索文件
【发布时间】:2011-07-01 09:29:38
【问题描述】:

我实现了这段代码,但我再次无法搜索子目录。

     procedure TFfileSearch.FileSearch(const dirName:string);
     begin
//We write our search code here
  if FindFirst(dirName,faAnyFile or faDirectory,searchResult)=0 then
  begin
    try
      repeat
      ShowMessage(IntToStr(searchResult.Attr));
        if (searchResult.Attr and faDirectory)=0 then   //The Result is a File
        //begin
          lbSearchResult.Items.Append(searchResult.Name)
         else 
         begin
            FileSearch(IncludeTrailingBackSlash(dirName)+searchResult.Name);
           //
         end;
       until FindNext(searchResult)<>0
     finally
     FindClose(searchResult);
     end;
   end;
   end;
    procedure TFfileSearch.btnSearchClick(Sender: TObject);
   var
 filePath:string;
begin
lbSearchResult.Clear;
if Trim(edtMask.Text)='' then
  MessageDlg('EMPTY INPUT', mtWarning, [mbOK], 0)
else
begin
  filePath:=cbDirName.Text+ edtMask.Text;
  ShowMessage(filePath);
  FileSearch(filePath);

end;

结束;

我正在 E:\ 驱动器中搜索 *.ini 文件。所以最初的 filePath 是 E:*.ini。 但代码不会搜索 E:\ 驱动器中的目录。如何纠正?

提前致谢

【问题讨论】:

标签: delphi delphi-7


【解决方案1】:

您不能在对FindFirst 的调用中对文件扩展名应用限制。如果这样做,则不会枚举目录。相反,您必须在代码中检查匹配的扩展名。试试这样的:

procedure TMyForm.FileSearch(const dirName:string);
var
  searchResult: TSearchRec;
begin
  if FindFirst(dirName+'\*', faAnyFile, searchResult)=0 then begin
    try
      repeat
        if (searchResult.Attr and faDirectory)=0 then begin
          if SameText(ExtractFileExt(searchResult.Name), '.ini') then begin
            lbSearchResult.Items.Append(IncludeTrailingBackSlash(dirName)+searchResult.Name);
          end;
        end else if (searchResult.Name<>'.') and (searchResult.Name<>'..') then begin
          FileSearch(IncludeTrailingBackSlash(dirName)+searchResult.Name);
        end;
      until FindNext(searchResult)<>0
    finally
      FindClose(searchResult);
    end;
  end;
end;

procedure TMyForm.FormCreate(Sender: TObject);
begin
  FileSearch('c:\windows');
end;

【讨论】:

  • 澄清一下,“您不能对文件扩展名应用限制”,因为这样就不会枚举目录。
  • @Sertac 谢谢。我不准确,并相应地更新了答案。
  • 此外,即使找到了一个目录,比如C:\SomeDir,也无法搜索`C:*.ini\SomeDir`。
  • 谢谢大家。奇怪的是,在绞尽脑汁之后,我也想通了。我想我应该早点检查一下。
  • 其实你可以指定扩展名但是你需要使用两个搜索变量。一次搜索只需使用 faDirectory 搜索 .,这样您就可以获得所有文件夹。第二个将在每个找到的文件夹中进行特定搜索...
【解决方案2】:

我建议这样做:

uses
  System.Types,
  System.IOUtils;

procedure TForm7.Button1Click(Sender: TObject);
var
  S: string;
begin
  Memo1.Lines.Clear;
  for S in TDirectory.GetFiles('C:\test', '*.bmp', TSearchOption.soAllDirectories) do
    Memo1.Lines.Add(S);
  Showmessage('Finished!');
end;

【讨论】:

  • 嗨!为您粘贴的代码提供一些解释会非常好。谢谢!
  • 虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
【解决方案3】:

我讨厌那些使用 FindFirst/FindNext 的递归解决方案,我认为有些人甚至忘记使用 FindClose 来清理资源很麻烦。所以,为了它的乐趣,一个应该实用的非递归解决方案......

procedure FindDocs(const Root: string);
var
  SearchRec: TSearchRec;
  Folders: array of string;
  Folder: string;
  I: Integer;
  Last: Integer;
begin
  SetLength(Folders, 1);
  Folders[0] := Root;
  I := 0;
  while (I < Length(Folders)) do
  begin
    Folder := IncludeTrailingBackslash(Folders[I]);
    Inc(I);
    { Collect child folders first. }
    if (FindFirst(Folder + '*.*', faDirectory, SearchRec) = 0) then
    begin
      repeat
        if not ((SearchRec.Name = '.') or (SearchRec.Name = '..')) then
        begin
          Last := Length(Folders);
          SetLength(Folders, Succ(Last));
          Folders[Last] := Folder + SearchRec.Name;
        end;
      until (FindNext(SearchRec) <> 0);
      FindClose(SearchRec);
    end;
    { Collect files next.}
    if (FindFirst(Folder + '*.doc', faAnyFile - faDirectory, SearchRec) = 0) then
    begin
      repeat
        if not ((SearchRec.Attr and faDirectory) = faDirectory) then
        begin
          WriteLn(Folder, SearchRec.Name);
        end;
      until (FindNext(SearchRec) <> 0);
      FindClose(SearchRec);
    end;
  end;
end;

虽然它似乎因为使用动态数组而占用了大量内存,但递归方法将完全一样,但递归发生在堆栈上!此外,使用递归方法,为 all 局部变量分配空间,而我的解决方案仅为文件夹名称分配空间。
当您检查速度时,两种方法都应该一样快。不过,递归方法更容易记住。您也可以使用 TStringList 代替动态数组,但我只是喜欢动态数组。
我的解决方案还有一个技巧:它可以在多个文件夹中搜索!我只用一个根初始化了 Folders 数组,但您可以轻松地将其长度设置为 3,并将 Folders[0] 设置为 C:\,Folders[1] 设置为 D:\,Folders[2] 设置为 E:\,然后将在多个磁盘上搜索!

顺便说一句,用您想要执行的任何逻辑替换 WriteLn() 代码...

【讨论】:

  • 如果不使用SetLength(.., Length()+1),我会投赞成票。
  • SetLength(.., Length()+1) 有什么问题?你宁愿分配一个更大的缓冲区吗?改用链表? Ot 一个 TStringList 来维护文件夹列表?有很多选项可供选择。
  • 字符串列表是我的选择。 SetLength(.., Length()+1)容易​​导致内存碎片。
  • 没错,但字符串列表也是如此,尽管字符串列表确实分配了稍大的数据块。再说一次,这些块是 4 个字节的倍数,因为从技术上讲,字符串是指向 PChar 的指针。所以实际的字符串分配仍然会增加内存碎片。另一方面,当函数结束时,它应该已经取消分配它分配的所有内存,从而再次清理碎片。
  • 你不懂地址空间碎片。发生的情况是,您的地址空间中出现了 MM 无法重用的小漏洞,最终无法找到大的连续地址空间块。您还给 MM 重复使用带来了问题。
【解决方案4】:

这个文件搜索的问题是会无限循环,FindClose就好像不存在一样。

【讨论】:

    【解决方案5】:
    procedure FindFilePattern(root:String;pattern:String);
    var
      SR:TSearchRec;
    begin
      root:=IncludeTrailingPathDelimiter(root);
      if FindFirst(root+'*.*',faAnyFile,SR) = 0 then
      begin
          repeat
              Application.ProcessMessages;
              if ((SR.Attr and faDirectory) = SR.Attr ) and (pos('.',SR.Name)=0) then
                 FindFilePattern(root+SR.Name,pattern)
              else
              begin
               if pos(pattern,SR.Name)>0 then Form1.ListBox1.Items.Add(Root+SR.Name);
              end;
          until FindNext(SR)<>0;
      end;
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      FindFilePattern('C:\','.exe');
    end;
    

    这会递归搜索显示包含特定模式的文件名的所有文件夹。

    【讨论】:

    • 此代码比较文件属性和名称不正确,并且正在泄漏搜索句柄。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-17
    • 2023-04-10
    • 1970-01-01
    • 2021-05-25
    • 2021-01-25
    • 1970-01-01
    相关资源
    最近更新 更多