演示两种方法来遍历文件

时间:2022-08-13 11:25:24

 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.