omikron48 Posted February 24, 2010 Share Posted February 24, 2010 (edited) I recently recoded a stack based search function of mine. In the process of recoding, I noticed that I was making two passes per directory, one for file searching and another for listing subdirectories. I was trying to eliminate the need for a second pass by using StringRegExp for the filenames returned by FileFindNextFile, when I observed that FileFindFirstFile doesn't use regular expressions like StringRegExp for matching searches.What I want to know is: Is it possible to eliminate the need for a second pass and still support the kind of matching used by FileFindFirstFile?Here is the code which does two passes:expandcollapse popupOpt("MustDeclareVars", 1) ;Put somewhere at the start of your script ;### STACK DEFINITION ### Global $RESULT_MAX = 0xFFF ;maximum size of the returned array Global $STACK_MAX = 0xFFF ;maximum size of the search stack Global $STACK[$STACK_MAX] ;the search stack $STACK[0] = 0 ;stack element counter Func _Push($var) ;Utility function. DO NOT CALL If $STACK[0] < $STACK_MAX - 1 Then $STACK[0] += 1 $STACK[$STACK[0]] = $var Else Return 1 EndIf Return 0 EndFunc Func _Pop() ;Utility function. DO NOT CALL If $STACK[0] > 0 Then $STACK[0] -= 1 Return $STACK[$STACK[0] + 1] Else SetError(1) EndIf Return 0 EndFunc ;### END STACK DEFINITION ### ;### SEARCH FUNCTION ### ;Returns array containing full path of matched files, with [0] containing the count of returned elements. ; ;$path = start location of search ; ;$filter = expression to use for file matching (e.g. *.txt) ; ;$directories = set to true if directories are to be included in the search results ; Default set to True ; ;$fExclude = attributes (look at FileGetAttrib) used to exclude files from search (e.g. if you want to skip read-only or hidden files) ; Default set to empty string ; ;$dExclude = attributes (look at FileGetAttrib) used to exclude folder from recursive search (e.g. if you want to skip searching system or hidden folders) ; Default set to empty string ; ;$depth = folder depth to limit search ; Default set to -1 ; ; Values: ; 0 -> current folder only ; n -> search up to n folders deep ; -1 -> search in all subfolders ; Func _Search($path, $filter, $directories = True, $fExclude = "", $dExclude = "", $depth = -1) ;store current working directory Local $current = @WorkingDir ;change working directory If FileChangeDir($path) Then ;add "\" to end of path value if needed If StringCompare(StringRight($path, 1), "\") <> 0 Then $path &= "\" EndIf Local $array[2] = [$path, 0] ;push initial search path to stack _Push($array) ;call utility search function Local $result = _SearchUtil($filter, $directories, $fExclude, $dExclude, $depth) ;revert working directory FileChangeDir($current) Return $result Else SetError(1) EndIf ;revert working directory FileChangeDir($current) Local $empty[2] = [0, "NO RESULTS"] Return $empty EndFunc Func _SearchUtil($filter, $directories, $fExclude, $dExclude, $depth) ;Utility function. DO NOT CALL Local $result[$RESULT_MAX] Local $search, $fname, $attrib Local $error, $extended Local $array[2], $temp[2] $result[0] = 0 While 1 ;pop next search path from stack $array = _Pop() ;exit if empty stack If @error == 1 Then ExitLoop EndIf ;change working directory FileChangeDir($array[0]) ;search contents of current directory $search = FileFindFirstFile($filter) While 1 $fname = FileFindNextFile($search) $error = @error $extended = @extended If $error == 1 Then ExitLoop EndIf ;skip processing if directory is not included in search results If $extended == 1 And Not $directories Then ContinueLoop EndIf ;add file to results if it is not excluded from search $attrib = FileGetAttrib($fname) If _IsIncluded($attrib, $fExclude) Then $result[0] += 1 $result[$result[0]] = $array[0] & $fname EndIf WEnd FileClose($search) ;add subdirectories to stack if not excluded from search $search = FileFindFirstFile("*") While 1 $fname = FileFindNextFile($search) $error = @error $extended = @extended If $error == 1 Then ExitLoop EndIf ;add directory to stack if not excluded from search $attrib = FileGetAttrib($fname) If $extended == 1 Then If _IsIncluded($attrib, $dExclude) Then $temp[0] = $array[0] & $fname & "\" $temp[1] = $array[1] + 1 _Push($temp) EndIf EndIf WEnd FileClose($search) WEnd Return $result EndFunc Func _IsIncluded($attrib, $exclude) ;Utility function. DO NOT CALL For $i = 1 To StringLen($exclude) If StringInStr($attrib, StringMid($exclude, $i, 1)) <> 0 Then Return False EndIf Next Return True EndFunc ;### END SEARCH FUNCTION ###Here is the code that does only one pass but only uses regular expressions:expandcollapse popupOpt("MustDeclareVars", 1) ;Put somewhere at the start of your script ;### STACK DEFINITION ### Global $RESULT_MAX = 0xFFF ;maximum size of the returned array Global $STACK_MAX = 0xFFF ;maximum size of the search stack Global $STACK[$STACK_MAX] ;the search stack $STACK[0] = 0 ;stack element counter Func _Push($var) ;Utility function. DO NOT CALL If $STACK[0] < $STACK_MAX - 1 Then $STACK[0] += 1 $STACK[$STACK[0]] = $var Else Return 1 EndIf Return 0 EndFunc Func _Pop() ;Utility function. DO NOT CALL If $STACK[0] > 0 Then $STACK[0] -= 1 Return $STACK[$STACK[0] + 1] Else SetError(1) EndIf Return 0 EndFunc ;### END STACK DEFINITION ### ;### SEARCH FUNCTION ### ;Returns array containing full path of matched files, with [0] containing the count of returned elements. ; ;$path = start location of search ; ;$filter = expression to use for file matching (e.g. *.txt) ; ;$directories = set to true if directories are to be included in the search results ; Default set to True ; ;$fExclude = attributes (look at FileGetAttrib) used to exclude files from search (e.g. if you want to skip read-only or hidden files) ; Default set to empty string ; ;$dExclude = attributes (look at FileGetAttrib) used to exclude folder from recursive search (e.g. if you want to skip searching system or hidden folders) ; Default set to empty string ; ;$depth = folder depth to limit search ; Default set to -1 ; ; Values: ; 0 -> current folder only ; n -> search up to n folders deep ; -1 -> search in all subfolders ; Func _Search($path, $filter, $directories = True, $fExclude = "", $dExclude = "", $depth = -1) ;store current working directory Local $current = @WorkingDir ;change working directory If FileChangeDir($path) Then ;add "\" to end of path value if needed If StringCompare(StringRight($path, 1), "\") <> 0 Then $path &= "\" EndIf Local $array[2] = [$path, 0] ;push initial search path to stack _Push($array) ;call utility search function Local $result = _SearchUtil($filter, $directories, $fExclude, $dExclude, $depth) ;revert working directory FileChangeDir($current) Return $result Else SetError(1) EndIf ;revert working directory FileChangeDir($current) Local $empty[2] = [0, "NO RESULTS"] Return $empty EndFunc Func _SearchUtil($filter, $directories, $fExclude, $dExclude, $depth) ;Utility function. DO NOT CALL Local $result[$RESULT_MAX] Local $search, $fname, $attrib Local $error, $extended Local $array[2], $temp[2] $result[0] = 0 While 1 ;pop next search path from stack $array = _Pop() ;exit if empty stack If @error == 1 Then ExitLoop EndIf ;change working directory FileChangeDir($array[0]) ;search contents of current directory $search = FileFindFirstFile("*") While 1 $fname = FileFindNextFile($search) $error = @error $extended = @extended If $error == 1 Then ExitLoop EndIf $attrib = FileGetAttrib($fname) ;if directory If $extended == 1 Then ;add directory to stack if not excluded from search If _IsIncluded($attrib, $dExclude) Then $temp[0] = $array[0] & $fname & "\" $temp[1] = $array[1] + 1 _Push($temp) EndIf ;skip search filter processing if directories are not included in search results If Not $directories Then ContinueLoop EndIf EndIf ;add file to results if it fits search filter and is not excluded from search If StringRegExp($fname, $filter) And _IsIncluded($attrib, $fExclude) Then $result[0] += 1 $result[$result[0]] = $array[0] & $fname EndIf WEnd FileClose($search) WEnd Return $result EndFunc Func _IsIncluded($attrib, $exclude) ;Utility function. DO NOT CALL For $i = 1 To StringLen($exclude) If StringInStr($attrib, StringMid($exclude, $i, 1)) <> 0 Then Return False EndIf Next Return True EndFunc ;### END SEARCH FUNCTION ### Edited February 24, 2010 by omikron48 Link to comment Share on other sites More sharing options...
PsaltyDS Posted February 24, 2010 Share Posted February 24, 2010 That must be horribly slow, because of the use of nested arrays. Shouldn't the search function be a single recursive function that returns an array, without reference to any GLOBAL? All that pushing and popping to share data between functions is just not required. Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law Link to comment Share on other sites More sharing options...
omikron48 Posted February 25, 2010 Author Share Posted February 25, 2010 (edited) I haven't tried benchmarking my file search method against other recursive file searchers, so I have no clue how well mine performs. I eliminated the nested arrays part of my original script: expandcollapse popupOpt("MustDeclareVars", 1) ;Put somewhere at the start of your script ;### STACK DEFINITION ### Global $RESULT_MAX = 0xFFF ;maximum size of the returned array Global $STACK_MAX = 0xFFF ;maximum size of the search stack Global $STACK[$STACK_MAX][2] ;the search stack Global $STACK_COUNT = 0 ;stack element counter Func _Push($path, $depth) ;Utility function. DO NOT CALL If $STACK_COUNT < $STACK_MAX - 1 Then $STACK[$STACK_COUNT][0] = $path $STACK[$STACK_COUNT][1] = $depth $STACK_COUNT += 1 Else Return 1 EndIf Return 0 EndFunc Func _Pop(ByRef $path, ByRef $depth) ;Utility function. DO NOT CALL If $STACK_COUNT > 0 Then $path = $STACK[$STACK_COUNT - 1][0] $depth = $STACK[$STACK_COUNT - 1][1] $STACK_COUNT -= 1 Return 1 Else SetError(1) EndIf Return 0 EndFunc ;### END STACK DEFINITION ### Global $start = TimerInit() Global $result = _Search("c:\", "*.jpg", false, "", "", -1) Global $time = TimerDiff($start) Global $display = "" For $i = 1 To $result[0] $display &= $result[$i] & @CRLF Next MsgBox(0x2000, "Search Results: " & $result[0], "Time: " & _ConvertTime($time)) Func _ConvertTime($millis) Local $days = Int($millis / 86400000) $millis -= $days * 86400000 Local $hours = Int($millis / 3600000) $millis -= $hours * 3600000 Local $minutes = Int($millis / 60000) $millis -= $minutes * 60000 Local $seconds = Int($millis / 1000) $millis -= $seconds * 1000 Local $result = "" If $days > 0 Then $result &= $days & "d:" EndIf If $hours > 0 Then $result &= StringFormat("%.2d", $hours) & "h:" EndIf If $minutes > 0 Then $result &= StringFormat("%.2d", $minutes) & "m:" EndIf If $seconds > 0 Then $result &= StringFormat("%.2d", $seconds) Else $result &= "00" EndIf $result &= "." & StringFormat("%.3d", $millis) & "s" Return $result EndFunc ;### SEARCH FUNCTION ### ;Returns array containing full path of matched files, with [0] containing the count of returned elements. ; ;$path = start location of search ; ;$filter = expression to use for file matching (e.g. *.txt) ; ;$directories = set to true if directories are to be included in the search results ; Default set to True ; ;$fExclude = attributes (look at FileGetAttrib) used to exclude files from search (e.g. if you want to skip read-only or hidden files) ; Default set to empty string ; ;$dExclude = attributes (look at FileGetAttrib) used to exclude folder from recursive search (e.g. if you want to skip searching system or hidden folders) ; Default set to empty string ; ;$depth = folder depth to limit search ; Default set to -1 ; ; Values: ; 0 -> current folder only ; n -> search up to n folders deep ; -1 -> search in all subfolders ; Func _Search($path, $filter, $directories = True, $fExclude = "", $dExclude = "", $depth = -1) ;store current working directory Local $current = @WorkingDir ;change working directory If FileChangeDir($path) Then ;add "\" to end of path value if needed If StringCompare(StringRight($path, 1), "\") <> 0 Then $path &= "\" EndIf ;push initial search path to stack _Push($path, 0) ;call utility search function Local $result = _SearchUtil($filter, $directories, $fExclude, $dExclude, $depth) ;revert working directory FileChangeDir($current) Return $result Else SetError(1) EndIf ;revert working directory FileChangeDir($current) Local $empty[2] = [0, "NO RESULTS"] Return $empty EndFunc Func _SearchUtil($filter, $directories, $fExclude, $dExclude, $depth) ;Utility function. DO NOT CALL Local $result[$RESULT_MAX] Local $search, $fname, $attrib Local $error, $extended Local $ePath, $eDepth $result[0] = 0 While 1 ;pop next search path from stack _Pop($ePath, $eDepth) ;exit if empty stack If @error == 1 Then ExitLoop EndIf ;change working directory If FileChangeDir($ePath) Then ;search contents of current directory $search = FileFindFirstFile($filter) While 1 $fname = FileFindNextFile($search) $error = @error $extended = @extended If $error == 1 Then ExitLoop EndIf ;skip processing if directory is not included in search results If $extended == 1 And Not $directories Then ContinueLoop EndIf ;add file to results if it is not excluded from search $attrib = FileGetAttrib($fname) If $result[0] < $RESULT_MAX - 1 And _IsIncluded($attrib, $fExclude) Then $result[0] += 1 $result[$result[0]] = $ePath & $fname EndIf WEnd FileClose($search) ;process subdirectories if within depth parameter If $depth == -1 Or $eDepth < $depth Then ;add subdirectories to stack if not excluded from search $search = FileFindFirstFile("*") While 1 $fname = FileFindNextFile($search) $error = @error $extended = @extended If $error == 1 Then ExitLoop EndIf ;add directory to stack if not excluded from search $attrib = FileGetAttrib($fname) If $extended == 1 Then If _IsIncluded($attrib, $dExclude) Then _Push($ePath & $fname & "\", $eDepth + 1) EndIf EndIf WEnd FileClose($search) EndIf EndIf WEnd Return $result EndFunc Func _IsIncluded($attrib, $exclude) ;Utility function. DO NOT CALL For $i = 1 To StringLen($exclude) If StringInStr($attrib, StringMid($exclude, $i, 1)) <> 0 Then Return False EndIf Next Return True EndFunc ;### END SEARCH FUNCTION ### As for how my code is organized... Well, it's just the way I think when I'm coding. Majority of my coding experience is in Java, with a little bit of C and C#, so I'm used to handling classes and objects. Plus, my search method is stack based and non-recursive. Only _SearchUtil is actually using the stack information, _Search only pushes the first element. Originally, I'd have a stack object with Push and Pop methods, then I'd use a priming function (_Search) which kicks off the actual utility function (_SearchUtil), with additional functions defined for more complicated checks which are used in more than one place (_IsIncluded). It appears more organized to me, when put that way. EDIT: Forgot to factor the depth parameter when I did my recode the first time. Added it now. EDIT2: Wierd. The nested array stack implementation seems to consistently work faster than the non-nested one. Here's the corrected nested array implementation: expandcollapse popupOpt("MustDeclareVars", 1) ;Put somewhere at the start of your script ;### STACK DEFINITION ### Global $RESULT_MAX = 0xFFF ;maximum size of the returned array Global $STACK_MAX = 0xFFF ;maximum size of the search stack Global $STACK[$STACK_MAX] ;the search stack $STACK[0] = 0 ;stack element counter Func _Push($var) ;Utility function. DO NOT CALL If $STACK[0] < $STACK_MAX - 1 Then $STACK[0] += 1 $STACK[$STACK[0]] = $var Else Return 1 EndIf Return 0 EndFunc Func _Pop() ;Utility function. DO NOT CALL If $STACK[0] > 0 Then $STACK[0] -= 1 Return $STACK[$STACK[0] + 1] Else SetError(1) EndIf Return 0 EndFunc ;### END STACK DEFINITION ### Global $start = TimerInit() Global $result = _Search("c:\", "*.jpg", false, "", "", -1) Global $time = TimerDiff($start) Global $display = "" For $i = 1 To $result[0] $display &= $result[$i] & @CRLF Next MsgBox(0x2000, "Search Results: " & $result[0], "Time: " & _ConvertTime($time)) Func _ConvertTime($millis) Local $days = Int($millis / 86400000) $millis -= $days * 86400000 Local $hours = Int($millis / 3600000) $millis -= $hours * 3600000 Local $minutes = Int($millis / 60000) $millis -= $minutes * 60000 Local $seconds = Int($millis / 1000) $millis -= $seconds * 1000 Local $result = "" If $days > 0 Then $result &= $days & "d:" EndIf If $hours > 0 Then $result &= StringFormat("%.2d", $hours) & "h:" EndIf If $minutes > 0 Then $result &= StringFormat("%.2d", $minutes) & "m:" EndIf If $seconds > 0 Then $result &= StringFormat("%.2d", $seconds) Else $result &= "00" EndIf $result &= "." & StringFormat("%.3d", $millis) & "s" Return $result EndFunc ;### SEARCH FUNCTION ### ;Returns array containing full path of matched files, with [0] containing the count of returned elements. ; ;$path = start location of search ; ;$filter = expression to use for file matching (e.g. *.txt) ; ;$directories = set to true if directories are to be included in the search results ; Default set to True ; ;$fExclude = attributes (look at FileGetAttrib) used to exclude files from search (e.g. if you want to skip read-only or hidden files) ; Default set to empty string ; ;$dExclude = attributes (look at FileGetAttrib) used to exclude folder from recursive search (e.g. if you want to skip searching system or hidden folders) ; Default set to empty string ; ;$depth = folder depth to limit search ; Default set to -1 ; ; Values: ; 0 -> current folder only ; n -> search up to n folders deep ; -1 -> search in all subfolders ; Func _Search($path, $filter, $directories = True, $fExclude = "", $dExclude = "", $depth = -1) ;store current working directory Local $current = @WorkingDir ;change working directory If FileChangeDir($path) Then ;add "\" to end of path value if needed If StringCompare(StringRight($path, 1), "\") <> 0 Then $path &= "\" EndIf Local $array[2] = [$path, 0] ;push initial search path to stack _Push($array) ;call utility search function Local $result = _SearchUtil($filter, $directories, $fExclude, $dExclude, $depth) ;revert working directory FileChangeDir($current) Return $result Else SetError(1) EndIf ;revert working directory FileChangeDir($current) Local $empty[2] = [0, "NO RESULTS"] Return $empty EndFunc Func _SearchUtil($filter, $directories, $fExclude, $dExclude, $depth) ;Utility function. DO NOT CALL Local $result[$RESULT_MAX] Local $search, $fname, $attrib Local $error, $extended Local $array[2], $temp[2] $result[0] = 0 While 1 ;pop next search path from stack $array = _Pop() ;exit if empty stack If @error == 1 Then ExitLoop EndIf ;change working directory FileChangeDir($array[0]) ;search contents of current directory $search = FileFindFirstFile($filter) While 1 $fname = FileFindNextFile($search) $error = @error $extended = @extended If $error == 1 Then ExitLoop EndIf ;skip processing if directory is not included in search results If $extended == 1 And Not $directories Then ContinueLoop EndIf ;add file to results if it is not excluded from search $attrib = FileGetAttrib($fname) If _IsIncluded($attrib, $fExclude) Then $result[0] += 1 $result[$result[0]] = $array[0] & $fname EndIf WEnd FileClose($search) ;process subdirectories if within depth parameter If $depth == -1 Or $array[1] < $depth Then ;add subdirectories to stack if not excluded from search $search = FileFindFirstFile("*") While 1 $fname = FileFindNextFile($search) $error = @error $extended = @extended If $error == 1 Then ExitLoop EndIf ;add directory to stack if not excluded from search $attrib = FileGetAttrib($fname) If $extended == 1 Then If _IsIncluded($attrib, $dExclude) Then $temp[0] = $array[0] & $fname & "\" $temp[1] = $array[1] + 1 _Push($temp) EndIf EndIf WEnd FileClose($search) EndIf WEnd Return $result EndFunc Func _IsIncluded($attrib, $exclude) ;Utility function. DO NOT CALL For $i = 1 To StringLen($exclude) If StringInStr($attrib, StringMid($exclude, $i, 1)) <> 0 Then Return False EndIf Next Return True EndFunc ;### END SEARCH FUNCTION ### Edited February 25, 2010 by omikron48 Link to comment Share on other sites More sharing options...
PsaltyDS Posted February 25, 2010 Share Posted February 25, 2010 Interesting, and a useful exercise for me. Let me take a shot at a straight recursive function to compare the times... will post back later. Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted February 25, 2010 Moderators Share Posted February 25, 2010 Hi,I hope you do not mind me butting in. I wrote recursive searches with both stack and recursive forms - they timed out essentially the same (to within a few percent) when used on about 6000 files. However, I did use a dynamically increasing stack which doubled in size each time it was filled - this would have slowed the search version a bit as each resize obviously involved a ReDim.Interestingly I found that increasing the start size of the stack did not have too much effect on the overall timing - no doubt because the initial ReDims on a smallish stack did not cause too much of a hit on the performance.I finally decided to use the stack version as I try to avoid recursion if I possibly can - too many bad memories. M23  Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area  Link to comment Share on other sites More sharing options...
PsaltyDS Posted February 25, 2010 Share Posted February 25, 2010 GREAT (GREAT Recursion Enables Amazing Tech). This was what I came up with, once I finally had time to fool with it: expandcollapse popup#include <Array.au3> #include <File.au3> Opt("MustDeclareVars", 1) _Main() Func _Main() Local $iTimer = TimerInit() Local $aRET = _Search("c:\Temp", "Test*", True, -1) $iTimer = TimerDiff($iTimer) _ArrayDisplay($aRET, "Time = " & Round($iTimer / 1000, 3) & "sec") EndFunc ;==>_Main ;### SEARCH FUNCTION ### ;Returns array containing full path of matched files, with [0] containing the count of returned elements. ; ;$s_Path = start location of search, default is "" (current working directory) ; ;$s_Filter = expression to use for file matching (e.g. *.txt), default is "*" ; ;$f_Directories = set to true if directories are to be included in the search results ; Default set to True ; ;$i_Depth = folder depth to limit search ; -1 -> (Default) no limit, search in all subfolders ; 0 -> search current folder only ; n -> search up to n folders deep ; Func _Search($s_Path = "", $s_Filter = "*", $f_Directories = True, $i_Depth = -1) Local $h_Search, $s_Found, $s_Results = "", $a_Results[1] = [0], $s_RecursePath, $a_Recurse[1] = [0] ; Parameter checks If $s_Path Then If StringRight($s_Path, 1) <> "\" Then $s_Path &= "\" Else $s_Path = @WorkingDir & "\" EndIf ; Get search handle $h_Search = FileFindFirstFile($s_Path & $s_Filter) If $h_Search <> -1 Then ; Perform search of this directory While 1 $s_Found = FileFindNextFile($h_Search) If @error Then ExitLoop ; Check for not found If @extended = 1 Then ; Check for directory ; Directory If $f_Directories Then $s_Results &= $s_Path & $s_Found & "\|" Else ; File $s_Results &= $s_Path & $s_Found & "|" EndIf WEnd FileClose($h_Search) EndIf ; check recursion If $i_Depth <> 0 Then $h_Search = FileFindFirstFile($s_Path & "*") While 1 $s_Found = FileFindNextFile($h_Search) If @error Then ExitLoop If @extended Then $a_Recurse = _Search($s_Path & $s_Found, $s_Filter, $f_Directories, $i_Depth - 1) For $n = 1 To $a_Recurse[0] $s_Results &= $a_Recurse[$n] & "|" Next EndIf WEnd FileClose($h_Search) EndIf ; Create return array and return If StringRight($s_Results, 1) = "|" Then $s_Results = StringTrimRight($s_Results, 1) $a_Results = StringSplit($s_Results, "|") EndIf Return $a_Results EndFunc ;==>_Search I removed the exclusion logic because it was so extremely situational. Every parameter is optional, with the default being current working directory, "*" pattern, include directories (which are indicated in the results by a trailing "\"), and full recursion. Would like to see how the search times compare. Valuater's AutoIt 1-2-3, Class... Is now in Session!For those who want somebody to write the script for them: RentACoder"Any technology distinguishable from magic is insufficiently advanced." -- Geek's corollary to Clarke's law Link to comment Share on other sites More sharing options...
omikron48 Posted February 25, 2010 Author Share Posted February 25, 2010 (edited) My method takes about 2.34 times as long as your recursive method. The way yours and mine handles data is different so I'm not sure how much of the difference in speed is due to optimizations in data handling. Just the dropping of the exclude parameters would speed up the searching. What do these do? Local $a_Results[1] = [0], $s_RecursePath, $a_Recurse[1] = [0] EDIT: I was right, majority of the difference was with the handling of the data. I made some changes on my method based on how I saw you handle yours and the speed difference dropped. The new speed is 1.045 times the speed of your recursive search. I also dropped processing of the exclude parameters so the amount of processing would be roughly the same. Adjusted script: expandcollapse popup#include <Array.au3> Opt("MustDeclareVars", 1) ;Put somewhere at the start of your script ;### STACK DEFINITION ### Global $STACK_MAX = 1;0xFF ;maximum size of the search stack Global $STACK[$STACK_MAX] ;the search stack $STACK[0] = 0 ;stack element counter Func _Push($var) ;Utility function. DO NOT CALL If $STACK[0] > $STACK_MAX - 2 Then $STACK_MAX *= 2 ReDim $STACK[$STACK_MAX] EndIf $STACK[0] += 1 $STACK[$STACK[0]] = $var Return 1 EndFunc Func _Pop() ;Utility function. DO NOT CALL If $STACK[0] > 0 Then $STACK[0] -= 1 Return $STACK[$STACK[0] + 1] Else SetError(1) EndIf Return 0 EndFunc ;### END STACK DEFINITION ### Global $start = TimerInit() Global $result = _Search("c:\", "*.jpg", True, "", "", -1) Global $time = TimerDiff($start) _ArrayDisplay($result, "Time = " & _ConvertTime($time)) Func _ConvertTime($millis) Local $days = Int($millis / 86400000) $millis -= $days * 86400000 Local $hours = Int($millis / 3600000) $millis -= $hours * 3600000 Local $minutes = Int($millis / 60000) $millis -= $minutes * 60000 Local $seconds = Int($millis / 1000) $millis -= $seconds * 1000 Local $result = "" If $days > 0 Then $result &= $days & "d:" EndIf If $hours > 0 Then $result &= StringFormat("%.2d", $hours) & "h:" EndIf If $minutes > 0 Then $result &= StringFormat("%.2d", $minutes) & "m:" EndIf If $seconds > 0 Then $result &= StringFormat("%.2d", $seconds) Else $result &= "00" EndIf $result &= "." & StringFormat("%.3d", $millis) & "s" Return $result EndFunc ;### SEARCH FUNCTION ### ;Returns array containing full path of matched files, with [0] containing the count of returned elements. ; ;PARAMETERS: ; ;$path = start location of search ; ;$filter = expression to use for file matching (e.g. *.txt) ; ;$directories = set to true if directories are to be included in the search results ; Default set to True ; ;$fExclude = attributes (look at FileGetAttrib) used to exclude files from search (e.g. if you want to skip read-only or hidden files) ; Default set to empty string ; ;$dExclude = attributes (look at FileGetAttrib) used to exclude folder from recursive search (e.g. if you want to skip searching system or hidden folders) ; Default set to empty string ; ;$depth = folder depth to limit search ; Default set to -1 ; ; Values: ; 0 -> current folder only ; n -> search up to n folders deep ; -1 -> search in all subfolders ; ;RETURN VALUE: ; ;Success: Array of files and folders matching the search parameter. ; [0] contains the count of matched elements. ; matched folders end with "\" ; ;Failure: Returns 0. ; Sets @error to: ; 1 -> $path does not exist. ; 2 -> No matches found. ; Func _Search($path, $filter, $directories = True, $depth = -1);$fExclude = "", $dExclude = "", $depth = -1) ;check directory exists If FileExists($path) Then ;add "\" to end of path value if needed If StringCompare(StringRight($path, 1), "\") <> 0 Then $path &= "\" EndIf Local $result = "" Local $search, $fname, $attrib Local $array[2], $temp[2] $array[0] = $path $array[1] = $depth ;push initial search path to stack _Push($array) While 1 ;pop next search path from stack $array = _Pop() ;exit if empty stack If @error == 1 Then ExitLoop EndIf ;search contents of current directory $search = FileFindFirstFile($array[0] & $filter) If $search <> -1 Then While 1 $fname = FileFindNextFile($search) If @error == 1 Then ExitLoop EndIf ;skip processing if directory is not included in search results If @extended == 1 Then If Not $directories Then ContinueLoop Else $fname &= "\" EndIf EndIf ;add file to results if it is not excluded from search ; $attrib = FileGetAttrib($fname) ; If _IsIncluded($attrib, $fExclude) Then $result &= $array[0] & $fname & "|" ; EndIf WEnd FileClose($search) EndIf ;process subdirectories if within depth parameter If $array[1] <> 0 Then ;add subdirectories to stack if not excluded from search $search = FileFindFirstFile($array[0] & "*") While 1 $fname = FileFindNextFile($search) If @error == 1 Then ExitLoop EndIf ;add directory to stack if not excluded from search If @extended == 1 Then ; $attrib = FileGetAttrib($fname) ; If _IsIncluded($attrib, $dExclude) Then $temp[0] = $array[0] & $fname & "\" $temp[1] = $array[1] - 1 _Push($temp) ; EndIf EndIf WEnd FileClose($search) EndIf WEnd ;create return array If StringCompare(StringRight($result, 1), "|") == 0 Then $result = StringTrimRight($result, 1) $result = StringSplit($result, "|") Else $result = 0 SetError(2, 0, 0) EndIf Return $result Else SetError(1, 0, 0) EndIf EndFunc ;Func _IsIncluded($attrib, $exclude) ;Utility function. DO NOT CALL ; For $i = 1 To StringLen($exclude) ; If StringInStr($attrib, StringMid($exclude, $i, 1)) <> 0 Then ; Return False ; EndIf ; Next ; Return True ;EndFunc ;### END SEARCH FUNCTION ### EDIT2: Made stack self resizing. Edited March 27, 2010 by omikron48 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now