困惑我很久的递归文件遍历的一个疑问, 大家帮忙看看吧. 谢谢了(50分)

  • 主题发起人 飞来石
  • 开始时间

飞来石

Unregistered / Unconfirmed
GUEST, unregistred user!
算法如下
procedure SearchFile(path:string);
var
SearchRec:TSearchRec;
found:integer;
begin
found:=FindFirst(path+'*.*', faAnyFile, SearchRec);
while found=0 do
begin
if (SearchRec.Name<>'.') and (SearchRec.name<>'..') and (SearchRec.Attr =faDirectory) then
begin
Form1.Memo1.Lines.Add(path+SearchRec.Name);
SearchFile(path+SearchRec.Name+'/');
end
else
if (SearchRec.Name<>'.') and (SearchRec.name<>'..') then
Form1.Memo1.Lines.Add(path+SearchRec.Name);

found:=FindNext(SearchREc);
end;
FindClose(SearchRec);
end;

比如我搜索C:/Test目录, 目录下有3个文件: A.exe, B.exe, Z.exe还有一个文件夹Next, Nest文件夹下有一个文件C.exe

搜索出来的结果, 当然是
C:/test/a.exe
C:/test/b.exe
C:/test/Nest
C:/test/Next/C.exe
C:/test/Z.exe

由于是按照字母顺序搜索, 前面搜索到a.exe和b.exe时候都还没有产生递归调用, 但是搜索到目录Next之后, 函数会递归, 进入C:/test/Next并搜索其下的内容.

那么现在问题出来了, 函数是怎么又回到上层的C:/test/的呢?添加完Next目录下的内容, 是执行的found:=FindNext(SearchREc);由于FindFirst已经指向了C:/test/Next, 那么又是如何回到上层去的呢?
 
1、当found不为0时,函数执行完FindClose返回上一层
2、搜索的信息保存在SearchRec里,他是一个保存在堆栈里的局部变量,
尽管发生递归调用,但当前SearchRec的内容只会被循环中的FindNext
以及之后的FindClose所改变,不会受递归调用产生的FindFirst、
FindNext、FindClose影响,因为影响的根本不是同一个变量。
 
这上N年前写的,希望对你有帮助。

//----1. 从 搜 索 记 录 中 判 断 是 否 是 子 目 录。
function TCompressForm.IsValidDir(SearchRec:TSearchRec):Boolean;
begin
if ((SearchRec.Attr=16) or (SearchRec.Attr=17)) and
(SearchRec.Name<>'.') and (SearchRec.Name<>'..') then
Result:=True
else
Result:=False;
end;

{----2. 查 询 主 体 函 数。
----Mainpath: 指定的查询目录;
----Filename: 欲查询的文件;
----SubDirInclude: 是否查找子目录;
----FoundFile: 文件名列表;}

function TCompressForm.SearchFile(MainPath:string; MainPathLen:integer;
SubDirInclude:Boolean; FileNameStream:string;
var FoundFile:TStringList):Boolean;
var
i : integer;
Found : Boolean;
subdir1 : TStringList;
searchRec : TsearchRec;
tmpString : string;
begin
found:=false;
if Trim(FileNameStream)<>'' then
begin
subdir1:=TStringList.Create; //字符串列表必须动态生成
//找出所有下级子目录。
if (FindFirst(MainPath +'*.*', faDirectory, SearchRec)=0) then
repeat
if IsValidDir(SearchRec) then
subdir1.Add(SearchRec.Name);
until (FindNext(SearchRec)<>0);
FindClose(SearchRec);

//查找当前目录。
if (FindFirst(MainPath +FileNameStream, faAnyFile, SearchRec)=0) then
begin
repeat
if ((SearchRec.Attr<>16) and (SearchRec.Attr<>17)) then begin
found:=true;
tmpString:= MainPath +SearchRec.name;
FoundFile.Add(copy(tmpString,MainPathLen+1,Length(tmpString)-MainPathLen));
end;
until (FindNext(SearchRec)<>0);
end;
FindClose(SearchRec);
//这是递归部分,查找各子目录。
if SubDirInclude=TRUE then
for i:=0 to subdir1.Count -1 do
found:=Searchfile(MainPath+subdir1.Strings+'/' ,MainPathLen,SubDirInclude,
FileNameStream,FoundFile)or found;
//资源释放并返回结果。
subdir1.Free;
end;
result:=found;
end;
 
FindFirst是用来寻找目标目录下的第一个文件,FindNext则是寻找下一个
TSearchRec是一个文件信息的纪录,当FindFirst返回SearchRec时,你可以通过SearchRec.Name获取文件名,以及SearchRec.Size获取文件大小等信息.
也就是说先找到了第一个文件夹,然后在第一个文件夹下面找第一个文件夹,往下推,如果没有了,再返回上一层,找第二个,再找第二个里的第一个,就是这个递归吧。。。。是以文件名顺序,不是以文件类型来找的吧。
 
递归调用,会不停的把地址压栈,函数返回后出栈继续执行。
所以,如果是死递归,就是只压栈不出栈,最终会出现堆栈溢出,然后程序就over了。
所以,递归不可能是无限递归下去,栈容量是有限的。
由于频繁的压栈出栈,所以递归的效率不高。
 
顶部