⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unit1.pas

📁 该例子演示了两种方法来遍历文件.采用Delphi编写
💻 PAS
字号:
{
演示两种方法来遍历文件

两个函数:
EnumFileInQueue     队列遍历
EnumFileInRecursion 递归遍历

版本:v1.0

作者:cenjoy 温校宏
邮箱:cenjoyer@163.com

说明:
看见很多遍历文件的文章都是用递归实现,可是
当子目录非常非常多时,就很容易堆栈溢出.
而使用队列的方法就不会出现这种问题.
不过使用队列这种方法也不是我想出来的,
我从书上看到的,因为发现还有很多人不知道,
所以把它贡献出来.

未解决问题:
1.如何统计遍历的时间和搜索结果
(如何知道用CreateThread创建的线程何时结束)

2.你来说吧~~~~

如果你能解决这些问题,希望你能发一份给我!!
}
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Contnrs, FileCtrl, ExtCtrls;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    DriveComboBox1: TDriveComboBox;
    Label1: TLabel;
    Bevel1: TBevel;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  private
    { Private declarations }
    hThreadHandle1:THandle;
    hThreadHandle2:THandle;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
{用队列的方法遍历文件}
function EnumFileInQueue(path:PChar):Longint;stdcall;
var
    searchRec:TSearchRec;
    found:Integer;
    tmpStr:String;
    curDir:PChar;
    dirs:TQueue;
begin
    Result:=0;//查找结果(文件数)
    dirs:=TQueue.Create;//创建目录队列
    dirs.Push(path);//将起始搜索路径入队
    curDir:=dirs.Pop;//出队
    {开始遍历,直至队列为空(即没有目录需要遍历)}
    while (curDir<> nil) do
    begin
        //加上搜索后缀,得到类似'c:\*.*' 、'c:\windows\*.*'的搜索路径
        tmpStr:=StrPas(curDir)+'\*.*';
        //在当前目录查找第一个文件、子目录
        found:=FindFirst(tmpStr,faAnyFile,searchRec);
        while found=0 do
        //找到了一个文件或目录后
        begin
            //如果找到的是个目录
            if (searchRec.Attr and faDirectory)<>0 then
            begin
                {在搜索非根目录(C:\、D:\)下的子目录时会出现'.','..'的"虚拟目录"
                 大概是表示上层目录和下层目录吧。。。要过滤掉才可以}
                if (searchRec.Name <> '.') and (searchRec.Name <> '..') then
                begin
                    {由于查找到的子目录只有个目录名,所以要添上上层目录的路径
                     searchRec.Name = 'Windows';
                     tmpStr:='c:\Windows';
                     加个断点就一清二楚了
                     }
                    tmpStr:=StrPas(curDir)+'\'+searchRec.Name;
                    {将搜索到的目录入队。让它先晾着。
                    因为TQueue里面的数据只能是指针,所以要把string转换为PChar
                    同时使用StrNew函数重新申请一个空间存入数据,否则会使已经进
                    入队列的指针指向不存在或不正确的数据(tmpStr是局部变量)。}
                    dirs.Push(StrNew(PChar(tmpStr)));
                end;
            end
            //如果找到的是个文件
            else begin
                {Result记录着搜索到的文件数。可是我是用CreateThread创建线程
                来调用函数的,不知道怎么得到这个返回值。。。我不想用全局变量}
                Result:=Result+1;
                //把找到的文件加到Memo控件
                Form1.Memo1.Lines.Add(StrPas(curDir)+'\'+searchRec.Name);
            end;
            //查找下一个文件或目录
            found:=FindNext(searchRec);
        end;
        {当前目录找到后,如果队列中没有数据,则表示全部找到了;
        否则就是还有子目录未查找,取一个出来继续查找。}
        if dirs.Count > 0 then
            curDir:=dirs.Pop
        else
            curDir:=nil;
    end;
    //释放资源
    dirs.Free;
    FindClose(searchRec);
end;

{用递归的方法遍历文件}
function EnumFileInRecursion(path:PChar):Longint;stdcall;
var
    searchRec:TSearchRec;
    found:Integer;
    tmpStr:String;
begin
    Result:=0; //查找结果(文件数)
    //加上搜索后缀,得到类似'c:\*.*' 、'c:\windows\*.*'的搜索路径
    tmpStr:=StrPas(path)+'\*.*';
    //在当前目录查找第一个文件、子目录
    found:=FindFirst(tmpStr,faAnyFile,searchRec);
    while found=0 do
    //找到了一个文件或目录后
    begin
        //如果找到的是个目录
        if (searchRec.Attr and faDirectory)<>0 then
        begin
            {在搜索非根目录(C:\、D:\)下的子目录时会出现'.','..'的"虚拟目录"
            大概是表示上层目录和下层目录吧。。。要过滤掉才可以}
            if (searchRec.Name <> '.') and (searchRec.Name <> '..') then
            begin
                {由于查找到的子目录只有个目录名,所以要添上上层目录的路径
                searchRec.Name = 'Windows';tmpStr:='c:\Windows';
                加个断点就一清二楚了}
                tmpStr:=StrPas(path)+'\'+searchRec.Name;
                //自身调用,查找子目录,递归。。。。
                Result:=Result+EnumFileInRecursion(PChar(tmpStr));
            end;
        end
        //如果找到的是个文件
        {这个也是递归的结束条件,结束条件对于理解递归来说,相当重要}
        else begin
            {Result记录着搜索到的文件数。可是我是用CreateThread创建线程
            来调用函数的,不知道怎么得到这个返回值。。。我不想用全局变量}
            Result:=Result+1;
            //把找到的文件加到Memo控件
            Form1.Memo1.Lines.Add(StrPas(path)+'\'+searchRec.Name);
         end;
         //查找下一个文件或目录
        found:=FindNext(searchRec);
    end;
    //释放资源
    FindClose(searchRec);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
    dwThreadID:Cardinal;
    path:String;
begin
    if hThreadHandle1 = 0 then
    begin
        Memo1.Lines.Clear;
        //得到驱动盘的路径,这样可能啰嗦了些.
        path:=DriveComboBox1.Drive+':';
        //创建线程,使用递归方法遍历文件
        hThreadHandle1:=CreateThread(nil,0,@EnumFileInRecursion,StrNew(PChar(path)),0,dwThreadID);
        {@EnumFileInRecursion是函数的地址
        PChar('c:')是函数的参数,以指针传递
        不能直接使用@EnumFileInRecursion('c:')}
    end else
    begin
        TerminateThread(hThreadHandle1,0);
        CloseHandle(hThreadHandle1);
        hThreadHandle1 := 0;
    end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
//结束线程
    if hThreadHandle1 <> 0 then
    begin
        TerminateThread(hThreadHandle1,0);
        CloseHandle(hThreadHandle1);
        hThreadHandle1 := 0;
    end;

    if hThreadHandle2 <> 0 then
    begin
        TerminateThread(hThreadHandle2,0);
        CloseHandle(hThreadHandle2);
        hThreadHandle2 := 0;
    end;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
    dwThreadID:Cardinal;
    path:String;
begin
    if hThreadHandle2 = 0 then
    begin
        Memo1.Lines.Clear;
        //得到驱动盘的路径,这样可能啰嗦了些.
        path:=DriveComboBox1.Drive+':';
        //创建线程,使用队列方法遍历文件
        hThreadHandle2:=CreateThread(nil,0,@EnumFileInQueue,StrNew(PChar(path)),0,dwThreadID);
        {@EnumFileInRecursion是函数的地址
        PChar('c:')是函数的参数,以指针传递
        不能直接使用@EnumFileInRecursion('c:')}
    end
    else begin
        TerminateThread(hThreadHandle2,0);
        CloseHandle(hThreadHandle2);
        hThreadHandle2 := 0;
    end;
end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose:=false;
//结束线程
    if hThreadHandle1 <> 0 then
    begin
        TerminateThread(hThreadHandle1,0);
        CloseHandle(hThreadHandle1);
        hThreadHandle1 := 0;
    end;

    if hThreadHandle2 <> 0 then
    begin
        TerminateThread(hThreadHandle2,0);
        CloseHandle(hThreadHandle2);
        hThreadHandle2 := 0;
    end;
  CanClose:=true;
end;

end.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -