Sign in to follow this  
Followers 0
omikron48

File Search Matching

7 posts in this topic

#1 ·  Posted (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:

Opt("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:

Opt("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 by omikron48

Share this post


Link to post
Share on other sites



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.

:mellow:


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

Share this post


Link to post
Share on other sites

#3 ·  Posted (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:

Opt("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:

Opt("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 by omikron48

Share this post


Link to post
Share on other sites

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.

:mellow:


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

Share this post


Link to post
Share on other sites

Hi,

I hope you do not mind me butting in. :mellow:

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._______My UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Share this post


Link to post
Share on other sites

GREAT (GREAT Recursion Enables Amazing Tech). :(

This was what I came up with, once I finally had time to fool with it:

#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.

:mellow:


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

Share this post


Link to post
Share on other sites

#7 ·  Posted (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:

#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 by omikron48

Share this post


Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0