求一个遍历目录然后将文件路径写入数组的源码!是将全路径文件名写入数组
本帖最后由 xtanxin 于 2010-1-10 18:24 编辑我找到写文件的了,但是,如何不生成这个临时的文件,能直接写到数组?
自己算晕了,也没办法.
目前的笨办法是,先写到文件,在读出,导入数组.自己感觉确实很笨哟.
哪位大侠,给更好的算法/ _FileListToArray() 我也找到它了,但是这个函数是返回文件名或目录名,,我要的是完全的路径.
我是在做一个对比删除的功能,我又一个文件的保留列表,根据这个列表把,目标目录里面不符合的排除删掉.
但是,我保留列表里面不同的目录有重名的文件?
怎么办?难到要自己修改这个UDF函数?5555 郁闷了,见到里面的正则表达式,就晕菜了.下面的是拿出的函数,看着改不了,555#include-once
#include "FileConstants.au3"
Func _FileListToArray($sPath, $sFilter = "*", $iFlag = 0)
Local $hSearch, $sFile, $sFileList, $sDelim = "|"
$sPath = StringRegExpReplace($sPath, "[\\/]+\z", "") & "\" ; ensure single trailing backslash
If Not FileExists($sPath) Then Return SetError(1, 1, "")
If StringRegExp($sFilter, "[\\/:><\|]|(?s)\A\s*\z") Then Return SetError(2, 2, "")
If Not ($iFlag = 0 Or $iFlag = 1 Or $iFlag = 2) Then Return SetError(3, 3, "")
$hSearch = FileFindFirstFile($sPath & $sFilter)
If @error Then Return SetError(4, 4, "")
While 1
$sFile = FileFindNextFile($hSearch)
If @error Then ExitLoop
If ($iFlag + @extended = 2) Then ContinueLoop
$sFileList &= $sDelim & $sFile
WEnd
FileClose($hSearch)
If Not $sFileList Then Return SetError(4, 4, "")
Return StringSplit(StringTrimLeft($sFileList, 1), "|")
EndFunc;==>_FileListToArray 本帖最后由 afan 于 2010-1-8 03:04 编辑
回复 4# xtanxin
Func __FileListToArray($sPath, $sFilter = "*", $iFlag = 0)
Local $hSearch, $sFile, $sFileList, $sDelim = "|"
If Not StringRegExp($sPath, ':') Then $sPath = @ScriptDir & '\' & $sPath
$sPath = StringRegExpReplace($sPath, "[\\/]+\z", "") & "\" ; ensure single trailing backslash
If Not FileExists($sPath) Then Return SetError(1, 1, "")
If StringRegExp($sFilter, "[\\/:><\|]|(?s)\A\s*\z") Then Return SetError(2, 2, "")
If Not ($iFlag = 0 Or $iFlag = 1 Or $iFlag = 2) Then Return SetError(3, 3, "")
$hSearch = FileFindFirstFile($sPath & $sFilter)
If @error Then Return SetError(4, 4, "")
While 1
$sFile = FileFindNextFile($hSearch)
If @error Then ExitLoop
If ($iFlag + @extended = 2) Then ContinueLoop
$sFileList &= $sDelim & $sPath & $sFile
WEnd
FileClose($hSearch)
If Not $sFileList Then Return SetError(4, 4, "")
Return StringSplit(StringTrimLeft($sFileList, 1), "|")
EndFunc ;==>__FileListToArray afan 上面的代码可以输出全路径了,但是,结果只是当前目录下的文件名和目录,
子目录下的内容不遍历了. http://www.autoitx.com/forum.php?mod=viewthread&tid=1261
很早已前的作品。 回复 6# xtanxin
_FileListToArray() 本来就是不返回子目录的。我修改的__FileListToArray()当然也如此。你需要可以自己改 我试试吧,头疼,,顽固不化的作品,简单学习了一下,其他的也浏览了。
看来,
用哪个目录遍历输出到文件,然后再读取分串到数组这个办法,是目前我能理解的唯一的了。
遍历目录,涉及到自身函数的嵌套调用,如果用变量就成了覆盖了,除非是每嵌套一次变量在变一回?
给函数增加一个跟着循环走的参数? 关于子目录遍历搜索文件,我搜索了本论坛,看到主要有两种写法,稍做了测试:
测试标准:一个目录(你们叫文件夹,我们老同志目录叫习惯了)下分别创建10-10,000个子目录及500-500,000个文件。
一、第一类是caodongchun写的用dir /s 的方法,思路挺巧妙,在不做特殊过滤的情况,速度该算是最快的。
看了下代码感觉只有2个小问题:1是必须有可写的目录供写;2是如果搜索的目录与写临时记录文件的目录相含,返回的数组就可能多出一个无关文件。
如果你不需要支持正则,又能满足或避免上述2个小问题,建议使用。
二、第二类是本贴相关热心网友及“aotoit”(是用户名)写的用递归的方法,测试了autoit写的(因其注释清楚,代码更好读些),优点是支持正则,最大的缺点使用了递归(耗资源,受级数限制,文件太多的话跑不动),不支持非正则(不适合新手),正则判断应该也有小问题,将目录也加进去判断或许不是太妥当,这样的话只要目录名符合正则,其下的所有文件都成了符合条件的了。
如果你不能使用第一类的函数,但你要搜索的目录下文件不多,就用第二类吧。
如果这二类你还不够用,最好将第二类做非递归改写。递归我是极不喜欢使用的,au虽没有对应的栈push,pop指令,但可以使用数组模拟栈来改写。
Dim $afile ,$num
$num = 0
_filelist(@ScriptDir)
$temp = ""
For $i=0 To UBound($afile)-1
$temp &= $afile[$i]&@CRLF
Next
MsgBox(0,"",$temp)
Func _filelist($searchdir)
$search = FileFindFirstFile($searchdir & "\*")
If $search = -1 Then Return -1
While 1
$file = FileFindNextFile($search)
If @error Then
FileClose($search)
Return
ElseIf $file = "." Or $file = ".." Then
ContinueLoop
ElseIf StringInStr(FileGetAttrib($searchdir & "\" & $file), "D") Then
_filelist($searchdir & "\" & $file)
ContinueLoop
EndIf
$afile [$num] = $searchdir & "\"&$file
$num += 1
ReDim $afile
WEnd
EndFunc ;==>_filelist
目录历遍文件存入数组$afile 第一类型dir /s 方式,我刚稍做了修改,修正多出一个文件的bug, 另外,当存在临时文件时,做个备份(不破坏不改变原文件该是写程序要注意的)。
第二类型正在改为非递归,稍等。
;如果不要正则支持,且不需排除某些文件,用这个很快.
Func _FileSearchToArray($sPath, $sFilter = "*", $sFalg = 0, $sSub = True)
Local $sfilelist
Local $sOutdir = @ScriptDir
If StringRight($sOutdir,1)<>"\" Then $sOutdir = $sOutdir & "\"
If StringRight($sPath, 1) <> "\" Then $sPath = $sPath & "\"
;-----------修正bug: 使用dir时,如果搜索目录与临时目录有包含,会多出一个临时文件--------------
If $sSub Then
If StringLeft($sOutdir, StringLen($sPath))=$sPath Then
$sOutdir = @TempDir & "\"
If StringLeft($sOutdir, StringLen($sPath))=$sPath Then $sOutdir = @WindowsDir & "\"
EndIf
Else
If $sOutdir = $sPath Then
$sOutdir = @TempDir & "\"
If @TempDir=@ScriptDir Then $sOutdir = @WindowsDir & "\"
EndIf
EndIf
;-----------------------------------------------------------------------------------
Local $sOUT = $sOutdir & "temp_filelist.txt"
Local $bBak=False, $sBakFile=@tempdir&"\temp_$$$$bak.txt"
If FileExists($sOUT) Then ;原文件备份
FileMove($sOUT, $sBakFile, 1)
$bBak=True
EndIf
_FileCreate($sOUT)
If @error Then
$sfilelist = -1
If $bBak Then FileMove($sBakFile, $sOUT, 1)
Return $sfilelist;文件无法创建,$sfilelist = -1,此函数无法应用,因为必须创建临时文件
EndIf
If StringInStr(FileGetAttrib($sPath),"D")<=0 Then
$sfilelist = -2
If $bBak Then FileMove($sBakFile, $sOUT, 1)
Return $sfilelist;无此目录,返回-2,不用浪费时间了
EndIf
If $sSub Then
$sSub = " /s"
Else
$sSub = ""
EndIf
If StringReplace($sFilter," ","") = "" Then $sFilter = "*"
$sFalg = Int($sFalg)
If $sFalg<0 Or $sFalg>2 Then $sFalg=0;规范$sFalg
Select
Case $sFalg = 1;只文件
$sFalg = "/a:-d"
Case $sFalg = 2;只目录
$sFalg = "/a:d"
Case Else
$sFalg = "/a"
EndSelect
RunWait(@ComSpec & ' /c ' & 'dir "' & $sPath & $sFilter & '" ' & $sFalg & ' /b' & $sSub & ' > "' & $sOUT & '"', '', @SW_HIDE)
;Local $t=TimerInit()
_FileReadToArray($sOUT, $sfilelist)
If Not IsArray($sfilelist) Then
Local $sfilelist
$sfilelist = 0
If $bBak Then FileMove($sBakFile, $sOUT, 1)
Return $sfilelist;该目录无文件
Else
If $sfilelist[$sfilelist] = "" Then;最后一行
_ArrayDelete($sfilelist, $sfilelist)
;ReDim $sfilelist[$sfilelist]
$sfilelist = $sfilelist - 1
EndIf
EndIf
;ConsoleWrite(TimerDiff($t)/1000&@CRLF)
Local $x
If $sSub = "" Then
For $x = 1 To $sfilelist
$sfilelist[$x] = $sPath & $sfilelist[$x]
Next
EndIf
FileDelete($sOUT)
If $bBak Then FileMove($sBakFile, $sOUT, 1)
Return $sfilelist
EndFunc ;==>_FilesearchToArray 第二类型暂改为非递归如下。先不支持正则,支持正则的我等会再改一份。
用法(前面部分参数与_FileListToArray一致)如:
$array = myFileListToArray("c:\windows","*",0,True, "")
;遍历c:\windows下所有文件
$array = myFileListToArray("c:\windows","*",0,True, ".exe$")
;遍历c:\windows下所有文件,除了exe文件
$array = myFileListToArray("c:\windows","*",0,True, ".exe$|.dll$")
;遍历c:\windows下所有文件,除了.exe和.dll文件
Func myFileListToArray($s_dir, $s_filt = "*", $i_flag = 0, $b_subdir=True, $s_ex="")
If StringRight($s_dir,1)="\" Then $s_dir = StringTrimRight($s_dir,1)
If $i_flag<1 Or $i_flag>2 Then $i_flag=0;规范
Local $i_step = 40000 ;每$i_step个文件重新定义一次数组大小,当文件很多时,此值就发挥作用
Local $a_return[$i_step+1];初始数组大小,存放共有多少个文件,因为每次一直ReDim很慢的
Local $i_count = 1;记录找到多少个文件$i_count-1
Local $a_dir[$i_step+1];初始数组大小,存放共有多少个文件,因为每次一直ReDim很慢的
$a_dir = 1;做到第几个数组,代替递归
$a_dir = $s_dir
$i_dirCount = 1;共有多少个数组,代替递归用的变量
Local $a_dir_temp, $isdir, $a_temp
Local $file, $search
While $a_dir<=$i_dirCount;阿福提示,代替递归的方法
$s_dir = $a_dir[$a_dir]
If $b_subdir Then;子目录搜索
$a_dir_temp = _FileListToArray($s_dir, "*", 2)
If UBound($a_dir_temp)>1 Then
If $i_dirCount+$a_dir_temp>UBound($a_dir)-1 Then ReDim $a_dir+$i_step];每次都ReDim会很慢
For $i = 1 To UBound($a_dir_temp)-1
$a_dir[$i_dirCount+$i] = $s_dir &"\"& $a_dir_temp[$i]
Next
$i_dirCount += $a_dir_temp;共有多少个数组,即多少个目录
EndIf
EndIf
If ($i_flag = 2) And ($s_filt = "*" Or $s_filt = "*.*") And $b_subdir Then;和上面条件相同,不用再做一次
;ConsoleWrite("same"&@crlf)
Else
$a_dir_temp = _FileListToArray($s_dir, $s_filt, $i_flag)
EndIf
If UBound($a_dir_temp)>1 Then
If $i_Count+$a_dir_temp>UBound($a_return)-1 Then ReDim $a_return+$i_step]
;阿福提示:每次都ReDim会很慢
;ConsoleWrite($i_Count&','&$a_dir_temp&','&$i_Count+$a_dir_temp&','&UBound($a_return)-1&@CRLF)
For $i = 1 To UBound($a_dir_temp)-1
;其他条件
If $s_ex<>"" Then
$a_temp = StringSplit($s_ex, "|")
For $j= 1 To UBound($a_temp)-1
If StringRight($a_temp[$j],1)="$" Then
$a_temp[$j]=StringTrimRight($a_temp[$j],1)
If StringRight($a_dir_temp[$i], StringLen($a_temp[$j]))=$a_temp[$j] Then ContinueLoop(2)
Else
If StringInStr($a_dir_temp[$i], $a_temp[$j])>0 Then ContinueLoop(2)
EndIf
Next
EndIf
;---------------
$a_return[$i_Count] = $s_dir &"\"& $a_dir_temp[$i]
$i_Count += 1
Next
EndIf
$a_dir += 1
WEnd;代替递归结束
$a_return = $i_count - 1
ReDim $a_return[$i_count]
Return $a_return
EndFunc 上一个改成非递归的,没考虑到文件排序问题,稍做修改,使其与dir/s 排序一致。
此函数用法同上。
;此函数搜索结果排序与dir/s相同,即与_FileSearchToArray数组结果相同
;如果目录数非常多(实际测试如多于2000目录),因这个函数使用了_ArrayInsert函数,会慢些(不会慢太多),建议目录数超过2000的用myFileListToArray(),我的笔记本x61实测遍历10,000个文件7秒,而_FilesearchToArray()仅1秒多。
如果不需参数$s_ex支持(排除哪些文件),当然还是使用_FilesearchToArray()快。
;当然myFileListToArray()讨厌的排序与dir/s不同,对于数组优化比较不利。
Func myFileListToArray_DirSort($s_dir, $s_filt = "*", $i_flag = 0, $b_subdir=True, $s_ex="")
If StringRight($s_dir,1)="\" Then $s_dir = StringTrimRight($s_dir,1)
If $i_flag<1 Or $i_flag>2 Then $i_flag=0;规范
Local $i_step = 40000 ;每$i_step个文件重新定义一次数组大小,当文件很多时,此值就发挥作用
Local $a_return[$i_step+1];初始数组大小,存放共有多少个文件,因为每次一直ReDim很慢的
Local $i_count = 1;记录找到多少个文件$i_count-1
Local $a_dir;初始数组大小,存放共有多少个文件,因为每次一直ReDim很慢的
$a_dir = 1;做到第几个数组,代替递归
$a_dir = $s_dir
$i_dirCount = 1;共有多少个数组,代替递归用的变量
Local $a_dir_temp, $isdir, $a_temp
Local $file, $search
While $a_dir<UBound($a_dir);代替递归
$s_dir = $a_dir[$a_dir]
If $b_subdir Then;子目录搜索
$a_dir_temp = _FileListToArray($s_dir, "*", 2)
;_ArrayDisplay($a_dir_temp)
If UBound($a_dir_temp)>1 Then
For $i = 1 To UBound($a_dir_temp)-1
_ArrayInsert($a_dir, $a_dir+$i, $s_dir &"\"& $a_dir_temp[$i])
Next
;$i_dirCount += $a_dir_temp;共有多少个数组,即多少个目录
EndIf
EndIf
If ($i_flag = 2) And ($s_filt = "*" Or $s_filt = "*.*") And $b_subdir Then;和上面条件相同,不用再做一次
;ConsoleWrite("same"&@crlf)
Else
$a_dir_temp = _FileListToArray($s_dir, $s_filt, $i_flag)
EndIf
If UBound($a_dir_temp)>1 Then
;-----如果每找到一个文件就执行一个自定义函数,此处不一定要
If $i_Count+$a_dir_temp>UBound($a_return)-1 Then ReDim $a_return+$i_step];每次都ReDim会很慢
;-------------------------------------------------------
;ConsoleWrite($i_Count&','&$a_dir_temp&','&$i_Count+$a_dir_temp&','&UBound($a_return)-1&@CRLF)
For $i = 1 To UBound($a_dir_temp)-1
;其他条件
If $s_ex<>"" Then
$a_temp = StringSplit($s_ex, "|")
For $j= 1 To UBound($a_temp)-1
If StringRight($a_temp[$j],1)="$" Then
$a_temp[$j]=StringTrimRight($a_temp[$j],1)
If StringRight($a_dir_temp[$i], StringLen($a_temp[$j]))=$a_temp[$j] Then ContinueLoop(2)
Else
If StringInStr($a_dir_temp[$i], $a_temp[$j])>0 Then ContinueLoop(2)
EndIf
Next
EndIf
;---------------
;-----如果每找到一个文件就执行一个自定义函数,将此处改为自定义函数
$a_return[$i_Count] = $s_dir &"\"& $a_dir_temp[$i]
;-----------------------------------------------------
$i_Count += 1
Next
EndIf
$a_dir += 1
WEnd;代替递归结束
$a_return = $i_count - 1
;-----如果每找到一个文件就执行一个自定义函数,考虑返回 $i_count - 1(即多少个符合条件的文件)
ReDim $a_return[$i_count]
Return $a_return
;---------------------------------------------------------------
EndFunc 支持正则的目录遍历(非递归)
用法如:
;所有文件
myFileListToArray_StringReg($s_dir, ".*?", 0, True, "")
;所有文件不含.au3(任意位置)文件,如为扩展名,写成\.au3$
myFileListToArray_StringReg($s_dir, ".*?", 0, True, "\.au3")
;所有.au3文件
myFileListToArray_StringReg($s_dir, "\.au3$", 0, True, "")
Func myFileListToArray_StringReg($s_dir, $s_filt = ".*?", $i_flag = 0, $b_subdir=True, $s_ex="")
If StringRight($s_dir,1)="\" Then $s_dir = StringTrimRight($s_dir,1)
If $i_flag<1 Or $i_flag>2 Then $i_flag=0;规范
Local $i_step = 40000 ;每$i_step个文件重新定义一次数组大小,当文件很多时,此值就发挥作用
Local $a_return[$i_step+1];初始数组大小,存放共有多少个文件,因为每次一直ReDim很慢的
Local $i_count = 1;记录找到多少个文件$i_count-1
Local $a_dir;初始数组大小,存放共有多少个文件,因为每次一直ReDim很慢的
$a_dir = 1;做到第几个数组,代替递归
$a_dir = $s_dir
$i_dirCount = 1;共有多少个数组,代替递归用的变量
Local $a_dir_temp, $isdir, $a_temp
Local $file, $search
While $a_dir<UBound($a_dir);代替递归
$s_dir = $a_dir[$a_dir]
$a_dir_temp = _FileListToArray($s_dir, "*", 0)
If UBound($a_dir_temp) <= 1 Then
$a_dir += 1
ContinueLoop
EndIf
$i_dirCount = 1
For $i = 1 To UBound($a_dir_temp)-1
;$isdir=DirGetSize($s_dir &"\" & $a_dir_temp[$i])
$isdir = StringInStr(FileGetAttrib($s_dir&"\"&$a_dir_temp[$i]),"D")
If $b_subdir And $isdir>0 Then
_ArrayInsert($a_dir, $a_dir+$i_dirCount, $s_dir &"\"& $a_dir_temp[$i])
$i_dirCount += 1
EndIf
Select
Case $i_flag=1;文件
If $isdir>0 Then ContinueLoop
Case $i_flag=2;目录
If $isdir<=0 Then ContinueLoop
EndSelect
If Not StringRegExp($a_dir_temp[$i], $s_filt, 0) Then ContinueLoop;正则匹配
;这里可加其他条件
If $s_ex<>"" AndStringRegExp($a_dir_temp[$i], $s_ex, 0) Then ContinueLoop;排除文件名
;-------------------------
;-----如果每找到一个文件就执行一个自定义函数,将此处改为自定义函数
If $i_Count +1 > UBound($a_return)-1 Then ReDim $a_return
$a_return[$i_Count] = $s_dir &"\"& $a_dir_temp[$i]
;--------------------------------------
$i_Count += 1
Next
$a_dir += 1
WEnd;代替递归结束
$a_return = $i_count - 1
;-----如果每找到一个文件就执行一个自定义函数,考虑返回 $i_count - 1(即多少个符合条件的文件)
ReDim $a_return[$i_count]
Return $a_return
;---------------------------------------------------------------
EndFunc
页:
[1]
2