演示两种方法来遍历文件

    技术2022-05-11  84

     Unit1.dfm

    object  Form1: TForm1   Left   =   248   Top  =   107   BorderStyle  =  bsDialog  Caption  =  # 25991 # 20214 # 36941 # 21382 # 30340 # 20363 # 23376   ClientHeight  =   364   ClientWidth  =   478   Color  =  clBtnFace  Font.Charset  =  ANSI_CHARSET  Font.Color  =  clWindowText  Font.Height  =   - 12   Font.Name  =   ' Arial'   Font.Style  =  []  OldCreateOrder  =   False   OnCloseQuery  =  FormCloseQuery  PixelsPerInch  =   96   TextHeight  =   15    object  Label1: TLabel     Left   =   3     Top  =   343     Width  =   173     Height  =   15     Caption  =   ' by rockhan  irochan@163.com'     Font.Charset  =  ANSI_CHARSET    Font.Color  =  clWindowText    Font.Height  =   - 12     Font.Name  =   ' Arial'     Font.Style  =  [fsBold]    ParentFont  =   False    end    object  Bevel1: TBevel     Left   =   0     Top  =   320     Width  =   481     Height  =   18     Shape  =  bsBottomLine   end    object  Label2: TLabel     Left   =   8     Top  =   280     Width  =   225     Height  =   15     Caption  =  # 27809 # 26377 # 25214 # 21040 # 27494 # 26519 # 22806 # 20256 # 30340 # 30446 # 24405 ' ....'#35831#25442#30424#21518#20877#25214'...'    end    object  Memo1: TMemo     Left   =   0     Top  =   0     Width  =   473     Height  =   273     ScrollBars  =  ssVertical    TabOrder  =   0    end    object  Button1: TButton     Left   =   296     Top  =   304     Width  =   75     Height  =   25     Caption  =  # 36882 # 24402 # 36941 # 21382     TabOrder  =   1     OnClick  =  Button1Click   end    object  Button2: TButton     Left   =   384     Top  =   304     Width  =   75     Height  =   25     Caption  =  # 20572 # 27490     TabOrder  =   2     OnClick  =  Button2Click   end    object  Button3: TButton     Left   =   208     Top  =   304     Width  =   75     Height  =   25     Caption  =  # 38431 # 21015 # 36941 # 21382     TabOrder  =   3     OnClick  =  Button3Click   end    object  DriveComboBox1: TDriveComboBox     Left   =   48     Top  =   304     Width  =   145     Height  =   21     BevelInner  =  bvNone    BevelOuter  =  bvNone    TabOrder  =   4    end end

    Unit1.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;    Label2: TLabel;    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);                 form1.Label1.Caption : = (StrPas(curDir) + ' '+searchRec.Name);                  if  searchRec.Name  =   ' elementclient.exe '  then                begin                form1.Label2.Caption  : =  strpas(curdir);               form1.Button2.Click;                end;            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;    pp:integer;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);             form1.Label1.Caption : = (StrPas(path) + ' '+searchRec.Name);             pp : =  SendMessage(form1.Memo1.Handle,EM_LINEFROMCHAR, - 1 , 0 );            form1.Label2.Caption : =  form1.Memo1.Lines.Strings[pp - 1 ];             if  searchRec.Name  =   ' elementclient.exe '  then               begin                   form1.Label2.Caption : =  strpas(path);                   form1.Button2.Click;               end;            end;            //  if searchrec.Name <> '' then begin            //  showmessage('sfksdkfksdf');            //  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.

    最新回复(0)