Jump to content
Sign in to follow this  
therks

Another recursive FileList function

Recommended Posts

I know there's already several of these types of functions around the forum but there were some things I wanted to do different, so I wrote my own.

In the tests I've done this appears to be working fine, but I'd appreciate a second look.

The error returns and the first three parameters are the same as the vanilla _FileListToArray, but there are a couple new flags, and two new parameters.

The new flags include:

- enabling recursion (4)

- disabling file count in element [0] (8)

- enabling full path returns (16).

The new parameters are:

- $sCallback - this is the name of a function you want to run on each loop, it is passed an array with data relating to the current file search in the loop. If the callback function returns 0 then the item is processed normally. If it returns 1 the item is skipped (this can be used to skip entire folders being processed). If it returns -1 the entire function errors out with -1 and @extended is set to the callback function's @extended value.

- $sCallbackUserParam - just an extra parameter that can be passed through to the callback function.

Example with callback:

#include <Array.au3>
#include <_FileListToArrayRecursive.au3>

Global $sWFile
_FileListToArrayRecursive(@DesktopDir, '*', 4, '_Callback', 'Wfile')
If @error = -1 And @extended = 42 Then
    MsgBox(0, '', 'You have a W file:' & @LF & $sWFile)
Else
    MsgBox(0, '', 'No files/folders that start with W exist.')
EndIf

Global $aReadOnly = _FileListToArrayRecursive(@DesktopDir, '*', 1+4, '_Callback', 'readonly')
Global $aEmpty = _FileListToArrayRecursive(@DesktopDir, '*', 1+4, '_Callback', 'empty')

_ArrayDisplay($aReadOnly)
_ArrayDisplay($aEmpty)

Func _Callback($aParams)
    Switch $aParams[5]
        Case 'Wfile'
            If StringLeft($aParams[2], 1) == 'W' Then
                $sWFile = $aParams[1] & $aParams[2]
                Return SetExtended(42, -1)
            EndIf
        Case 'empty'
            If Not $aParams[3] Then
                If FileGetSize($aParams[0] & $aParams[1] & $aParams[2]) Then Return 1
            EndIf
        Case 'readonly'
            If Not $aParams[3] Then
                If Not StringInStr(FileGetAttrib($aParams[0] & $aParams[1] & $aParams[2]), 'R') Then Return 1
            EndIf
    EndSwitch
EndFunc

#include-once
; #FUNCTION# ====================================================================================================================
; Name...........: _FileListToArrayRecursive
; Description ...: Lists files and/or folders in a specified path recursively
; Syntax.........: _FileListToArrayRecursive($sPath[, $sFilter = '*'[, $iFlag = 0[, $sCallback = ''[, $sCallbackUserParam = '' ] ] ] ] )
; Parameters ....: $sPath             - Path to search
;                 $sFilter          - Optional: the filter to use, default is *
;                 $iFlag              - Optional: specifies options about return, add flags together
;                 |$iFlag =  0 (Default) Return both files and folders
;                 |$iFlag =  1 Return files only
;                 |$iFlag =  2 Return folders only
;                 |$iFlag =  4 Search subfolders (Enable recursion)
;                 |$iFlag =  8 Make array 0-based (Disable return count in first element)
;                 |$iFlag = 16 Return full paths (default only returns from the search folder down)
;                 $sCallback          - Optional: function to call on each loop. See remarks for details (default: none)
;                 $sCallbackUserParam - Optional: a parameter to pass to the Callback function (default: none)
; Return values .: @Error - 1 = Path not found or invalid
;                 |2 = Invalid $sFilter
;                 |3 = Invalid $iFilter
;                 |4 = No files found or folder inaccessible
;                 |5 = No files found or folder inaccessible
;                 |-1 = Callback function returned -1
; Author ........: Rob Saunders (therks at therks dot com)
; Modified.......:
; Remarks .......: If $sCallback is defined the function will be called on each loop and it can alter what files are recorded
;                 in the return array. The Callback function runs on every file result because it is called *before* the flag
;                 filter for files/folders ($iFlag 1 or 2).
;                   Return  0 = Item is added (depending on $iFlag 1 or 2)
;                   Return  1 = Item is skipped (note: if you skip a folder it will not be searched either)
;                   Return -1 = Stop searching. Main func returns false, @error = -1, @extended = Callback's @extended value
;                 The function is passed one parameter, a 6 item array with values as follows:
;                   [0] root search folder
;                   [1] current sub folder
;                   [2] current filename match
;                   [3] 1 if file is a folder; 0 if not
;                   [4] current file count
;                   [5] $sCallbackUserParam
;
; Related .......: _FileListToArray
; Link ..........:
; Example .......:
; Note ..........: Some parts from original function (by SolidSnake) and _FileListToArrayEx (by DXRW4E)
; ===============================================================================================================================

Func _FileListToArrayRecursive($sPath, $sFilter = '*', $iFlag = 0, $sCallback = '', $sCallbackUserParam = '')
    Local Const $FL_FILES = 1, $FL_FOLDERS = 2, $FL_RECURSE = 4, $FL_NOCOUNT = 8, $FL_FULLPATH = 16

    If Not StringInStr(FileGetAttrib($sPath), 'D') Then Return SetError(1, 1, '')
    If StringRegExp($sFilter, '[\\/:><\|]|(?s)\A\s*\z') Then Return SetError(2, 2, '')
    If $iFlag < 0 Or $iFlag > 1+2+4+8+16 Then Return SetError(3, 3, '')

    $sPath = StringRegExpReplace($sPath, '[\\/]+$', '') & '\'

    Local $sFileTrack, $iFileCount, $hParentSearch, $hSearch, $hSubCheck, $sSubDir, $sFile, $iIsFolder, $sRegExFilter, $sAddFullPath, _
        $iReturnFolders, $iReturnFiles, $iRecurse, $iReturnCount, $iSortOffset, _
        $aCallbackData[6], $vCallbackReturn, $iCallbackExt

    $aCallbackData[5] = $sCallbackUserParam

    If BitAND($iFlag, $FL_FULLPATH) Then $sAddFullPath = $sPath

    If BitAND($iFlag, $FL_RECURSE) Then
        $iRecurse = 1
        If StringReplace($sFilter, '*', '') Then ; If we ARE recursing, and the filter is something besides just * then initialize the regex filter
            $sRegExFilter = '(?i)^' & StringRegExpReplace(StringReplace(StringRegExpReplace($sFilter, '(\.|\||\+|\(|\)|\{|\}|\[|\]|\^|\$|\\)', "\\$1"), '?', '.'), '\*+', '.*') & '$'
            $sFilter = '*'
        EndIf
    EndIf

    If BitAND($iFlag, $FL_FILES) Then
        $iReturnFiles = 1
    ElseIf BitAND($iFlag, $FL_FOLDERS) Then
        $iReturnFolders = 1
    Else
        $iReturnFiles = 1
        $iReturnFolders = 1
    EndIf

    If BitAND($iFlag, $FL_NOCOUNT) Then
        $iReturnCount = 2 ; Param for StringSplit
        $iSortOffset = 0
    EndIf

    $hParentSearch = FileFindFirstFile($sPath & $sSubDir & $sFilter)
    If $hParentSearch = -1 Then Return SetError(4, 4, '')
    $hSearch = $hParentSearch
    While 1
        $sFile = FileFindNextFile($hSearch)
        If @error Then
            If $hParentSearch = $hSearch Then ExitLoop
            FileClose($hSearch)
            $hSearch -= 1
            $sSubDir = StringLeft($sSubDir, StringInStr(StringTrimRight($sSubDir, 1), '\', 0, -1))
            ContinueLoop
        EndIf
        $iIsFolder = @extended

        If $sCallback Then
            $aCallbackData[0] = $sPath
            $aCallbackData[1] = $sSubDir
            $aCallbackData[2] = $sFile
            $aCallbackData[3] = $iIsFolder
            $aCallbackData[4] = $iFileCount
            $vCallbackReturn = Call($sCallback, $aCallbackData)
            If @error = 0xDEAD And @extended = 0xBEEF Then
                FileClose($hSearch)
                FileClose($hParentSearch)
                Return SetError(5, 5, '')
            ElseIf $vCallbackReturn = -1 Then
                $iCallbackExt = @extended
                FileClose($hSearch)
                FileClose($hParentSearch)
                Return SetError(-1, $iCallbackExt, '')
            ElseIf $vCallbackReturn Then
                ContinueLoop
            EndIf
        EndIf

        If $iRecurse Then
            If ($iIsFolder And $iReturnFolders) Or (Not $iIsFolder And $iReturnFiles) Then
                If Not $sRegExFilter Or ($sRegExFilter And StringRegExp($sFile, $sRegExFilter)) Then
                    $sFileTrack &= '|' & $sAddFullPath & $sSubDir & $sFile
                    $iFileCount += 1
                EndIf
            EndIf

            If $iIsFolder Then
                $hSubCheck = FileFindFirstFile($sPath & $sSubDir & $sFile & '\' & $sFilter)
                If $hSubCheck = -1 Then ContinueLoop
                $sSubDir &= $sFile & '\'
                $hSearch = $hSubCheck
            EndIf
        Else
            If ($iIsFolder And $iReturnFolders) Or (Not $iIsFolder And $iReturnFiles) Then
                $sFileTrack &= '|' & $sAddFullPath & $sSubDir & $sFile
                $iFileCount += 1
            EndIf
        EndIf

    WEnd
    FileClose($hParentSearch)

    Local $aReturnList = StringSplit(StringTrimLeft($sFileTrack, 1), '|', $iReturnCount)
    If @error Then Return SetError(4, 4, '')
    Return $aReturnList
EndFunc

I'm still working on this as time goes by so the most up to date version can be found in my dropbox: http://db.tt/OqNgumLK

Edited by therks

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  

  • Similar Content

    • By sksbir
      Hi
      Trying this from autoit v3.3.14.5 and SCITE 3.19.102.1901.0 :
      help file , page "Language Reference - Variables" , sample autoit script about maps:

      Maps must be declared before use by defining their scope using the 'Global/Local/Static' keywords. Local $mControls[]
      So is my test script : only with this local declation.
      -check syntax is OK
      - running script : 

      test.au3" (13) : ==> Variable subscript badly formatted.: Local $mControls[] Local $mControls[^ ERROR ->14:51:49 AutoIt3.exe ended.rc:1
      any clue ?
    • By Blitzkid
      Hello, i want to search several directories for files with the largest numbers behind them (Like "video123") . They dont have a datatype. But there are also files with longer names and datatypes in these folders (Like "video778.mp4"). Is it possible to filter the _FileListToArray Syntax from
      to smth. like
       
      Here is my Code
      #include <ButtonConstants.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <array.au3> #include <File.au3> $filedir = @ScriptDir & "\" _checkfile() Func _checkfile() ConsoleWrite("______________________" & @CRLF) Local $arr[3] = ["music", "picture", "video"] For $i = 0 To UBound($arr) - 1 Local $arrayfiles = _FileListToArray($filedir & $arr[$i], $arr[$i] & "*", 1) If @error = 1 Then ConsoleWrite($arr[$i] & "Error 1") EndIf If @error = 4 Then ConsoleWrite($arr[$i] & "Error 2") ;Exit EndIf $arrayfilter = _ArrayMax($arrayfiles, 0, 1) Global $stringfiles = StringReplace($arrayfilter, $arr[$i], "") ConsoleWrite($arrayfilter & @CRLF) Next EndFunc ;==>_checkfile  
    • By jantograaf
      Hi all, 
      I'm trying to make a listing of all files in subdirectories on a specific level in a folder structure. Let me explain, visually, this will help things a lot.

      I have a folder structure like this:
       
      ROOT |--- SUBDIR 1 | |---- SUBDIR 1.1 | |----- SUBDIR 1.1.1 | |---- File1.ext | |----- SUBDIR 1.1.2 | |---- File2.ext | |----- SUBDIR 1.1.3 | |---- File2.ext | |----- SUBDIR 1.1.4 | |---- File2.ext | |----- SUBDIR 1.1.5 | |---- File2.ext | |---- SUBDIR 1.2 | |----- SUBDIR 1.2.1 | ..... | |---- SUBDIR 1.3 .... I use _FileListToArrayRec twice:
      - once to make an array of the specific directories I should be working at: I need all files on the x.x level, so it will go just until that depth using a negative integer for $iRecur
      - once again to create an array of all files found under that directory and its subdirectories (level x.x.x\files...)
      What happens now is that _FileListToArrayRec will always include all levels before the maximum depth is reached. The result would look like this
      Row 0 15 Row 1 Root\Subdir 1 Row 2 Root\Subdir 2 Row 3 Root\Subdir 3 Row 4 Root\Subdir 1\Subdir 1.1 Row 5 Root\Subdir 1\Subdir 1.2 Row 6 Root\Subdir 1\Subdir 1.3 ... Needless to say that when my second function iterates over this array, it will find all files twice. Once on the x level, once again on the x.x level. There is no way for me not to use the recursive option in the second iteration, since the files are actually in a subdirectory there.

      Where are the wizards of programming logic here? Since I can't seem to find a comprehensible or easily implementable solution for this issue.

      Thanks in advance and kind regards,

      Jan
    • By jmp
      Script running good but error in line 7.
      When i run this script :
      #include <IE.au3> #include <Array.au3> $oIE = _IEAttach ("Shop") $oTable = _IETableGetCollection ($oIE, 1) $aTableData = _IETableWriteToArray ($oTable) For $inumber = 1 To UBound($aTableData) -1 $table = $aTableData[4][$inumber] MsgBox(0, "", $table) Next I got Error: array variable has incorrect number of subscripts or subscript dimension range exceeded
×
×
  • Create New...