Jump to content

Improvement of included _FileListToArray function.


Tlem
 Share

Recommended Posts

Wow! Now this is huge change.

It looks like a filter could exclude a folder name from being returned by the function, but if $bRecursive is set, the files within that folder are still returned?

If exclude removes a folder name from the list, the files names within that folder are still returned. Maybe that's cool.

If you're retuning files only, should filters apply to folder names?

I'm just trying to figure out filters, excludes and the $iFlag all interact, or how they should interact?

A short explanation:

Exclude filtering is done only once at the end of recursive calls and it's done only on filenames and/or foldernames (not full path of files and folders).

It's a very simple extraction, so it's possible that a folder is extracted, but the files within this folders are returned.

Normally i can life with that behavior, cause extraction is normally done on filenames.

A possible solution maybe a additional exclude check shortly before the recursive call.

(before the line: $sFileList &= _FileListToArrayEx($sPathItem & $sFile, $sFilter, $iFlag, $bRecursiv, "", 0))

You have to prepare the $sExclude string and call -> If StringRegExp(StringRegExpReplace(... then ContinueLoop

(like the Exclude part)

This is surely more time consuming (every folder is checked) but maybe a alternative solution.

So this is the old question: What is more important, speed or comfort?

(and i don't know the answer) ;-)

Edited by BaKaMu
Link to comment
Share on other sites

  • Replies 265
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

So this is the old question: What is more important, speed or comfort?

Both , but being logical

my point of biew is if the folder is excluded the files cannot be return.

I don't know how much speed it will cost if no exclude is used but I am sure you will find a solution for not impacting the speed in this case

Nice work from everybody :)

Link to comment
Share on other sites

I think the debate is more simplicity versus complexity.

I've nothing against complexity, if it is useful, but all the new parameters open up a lot of combinations of interaction between $iFlag, $sFilter, and any specified exclude strings. I mentioned a few of the possibilities in my last post. I would think that how these parms all work together would need to be determined, and documented.

There is a bug in the exisitng production routine. If called with $iFlag as either 1 or 2, and no files/folders are found, it returns a one-element array, with nothing in the element. Only when called with an $iFlag of 0 will it return @error = 4.

The "simple" version in this thread suffers from the same problem, which is alleviated by adding the two tests at the end of the while loops as in the example below.

The recent "complex" version in this thread crashes under this circumstance. I'm unsure if the same two lines of code would also remedy that version.

Func _FileListToArray($sPath, $sFilter = "*", $iFlag = 0)
Local $hSearch, $sFile, $sFileList, $sDelim = "|"

If Not FileExists($sPath) Then Return SetError(1, 1, "")
If StringRegExp($sFilter, "[\\/:<>|]") Or (Not StringStripWS($sFilter, 8)) Then Return SetError(2, 2, "")
If StringRight($sPath, 1) <> "\" Then $sPath &= "\" 

If $iFlag > 3 Then
    $sDelim &= $sPath
    $iFlag -= 4
EndIf

$hSearch = FileFindFirstFile($sPath & $sFilter)
If @error Or $hSearch = -1 Then Return SetError(4, 4, "")


Switch $iFlag
    Case 0; Files and Folders
        While 1
            $sFile = FileFindNextFile($hSearch)
            If @error Then ExitLoop
            $sFileList &= $sDelim & $sFile
        WEnd
    Case 1; Files Only
        While 1
            $sFile = FileFindNextFile($hSearch)
            If @error Then ExitLoop
            If @extended Then ContinueLoop; bypass folder
            $sFileList &= $sDelim & $sFile
        WEnd
    Case 2; Folders Only
        While 1
            $sFile = FileFindNextFile($hSearch)
            If @error Then ExitLoop
            If @extended = 0 Then ContinueLoop; bypass file
            $sFileList &= $sDelim & $sFile
        WEnd
    Case Else
        Return SetError(3, 3, "")
EndSwitch
FileClose($hSearch)
If Not $sFileList Then Return SetError(4, 4, "")
Return StringSplit(StringTrimLeft($sFileList, 1), "|")
EndFunc;==>_FileListToArray
Edited by Spiff59
Link to comment
Share on other sites

Both , but being logical

my point of biew is if the folder is excluded the files cannot be return.

I don't know how much speed it will cost if no exclude is used but I am sure you will find a solution for not impacting the speed in this case

Nice work from everybody :)

Now i have made a new version, which do not return files/folders from excluded folders.

For this i removed the Exclude part at the end of the recursion and check now each returned file/folder name from FileFindNextFile.

To my surprise, the speed is not much lower (in some cases the speed is even higher).

So thank you for the good advise :-)

To all: Please check the new version for errors

; #FUNCTION# ================================================================================================
; Name:             _FileListToArrayEx2
; Description:      full compatible _FileListToArray replacement  (with more speed and additional features)
;                   additional: multi-path, multi-filter, multi exclude-filter, recursiv search
;                   optional full pathname
; Syntax:           _FileListToArrayEx2([$sPath = @ScriptDir, [$sFilter = "*", [$iFlag = 0, [$bRecursiv = False, [$sExclude = "", [$iFormat = 1]]]]]])
; Parameter(s):     $sPath = optional: path to generate filelist for, multi paths separated with semicolon (ex: "C:\";D:\Temp")
;                            if no path is given then @ScriptDir is used
;                   $sFilter = optional: The filter to use. (default: "*")
;                              multi filters separated with semicolon (ex: *.exe; *.txt will find all .exe and .txt files)
;                              (Search the Autoit3 manual for the word "WildCards" for details)
;                   $iFlag = Optional: specifies whether to return files, folders or both
;                            0 = (Default) Return both files and folders
;                            1 = Return files only
;                            2 = Return folders only
;                            $iFlag + 4 = Return Filenames and/or Folders incl full Path
;                   $bRecursiv = optional: true: recursive search in rootdir and subdirs
;                                          False (default): search only in rootdir
;                   $sExclude = optional: exclude a file/folder from the list by all or part of its name, various statements delimited with semicolon
;                               (ex: Unins* will remove all files/folders that start with Unins)
;                   $iFormat =  optional: return format
;                               0 = String ( "|" delimited)
;                               1 = (default) one-dimensional array, array[0] = number of files\folders returned
;                               2 = one-dimensional array, 0-based
; Requirement(s):   none
; Return Value(s):  on success: string or array (dependent on $iFormat)
; Author(s):        bernd670, Tlem, Spiff59, Zedna, KaFu, SmOke_N, GEOSoft, BaKaMu
; =================================================================================================
Func _FileListToArrayEx2($sPath = @ScriptDir, $sFilter = "*", $iFlag = 0, $bRecursiv = False, $sExclude = "", $iFormat = 1)
  Local $hSearch, $iPCount, $iFCount, $sFile, $TFlag = 0, $sFileList = "", $sTExclude = ""

  If $sPath = -1 Or $sPath = Default Then $sPath = @ScriptDir
  If $sFilter = -1 Or $sFilter = Default Then $sFilter = "*"
  If $iFlag = -1 Or $iFlag = Default Then $iFlag = 0
  If $bRecursiv = Default Then $bRecursiv = False
  If $sExclude = -1 Or $sExclude = Default Then $sExclude = ""
  If $iFormat = -1 Or $iFormat = Default Then $iFormat = 1

  If $sExclude Then ;prepare $sTExclude
    ;Strip leading and trailing spaces and spaces between semi colon delimiter
    $sTExclude = StringStripWS(StringRegExpReplace($sExclude, "\s*;\s*", ";"), 3)
    ;convert $sExclude to RegExp formated string
    $sTExclude = StringReplace($sTExclude, ".", "\.")
    $sTExclude = StringReplace($sTExclude, "?", ".")
    $sTExclude = StringReplace($sTExclude, "*", ".*?")
    $sTExclude = StringReplace($sTExclude, ";", "|")
  EndIf

  ;separate multi path
  Local $aPath = StringSplit($sPath, ';')
  ;separate multi filter
  Local $aFilter = StringSplit($sFilter, ';')

  For $iPCount = 1 To $aPath[0]
    Local $sPathItem = StringStripWS($aPath[$iPCount], 3) ;Strip leading and trailing spaces
    Local $sDelim = "|"  ;reset $sDelim

    If StringRight($sPathItem, 1) <> "\" Then $sPathItem = $sPathItem & "\" ;check for trailing "\"

    ;return full-path or filename only ($iFlag 0,1,2 = Filename, $iFlag 4,5,6 = full path)
    If $iFlag > 3 Then
      $sDelim &= $sPathItem
      $TFlag = $iFlag - 4
    Else
      $TFlag = $iFlag
    EndIf

    ;perform the search
    For $iFCount = 1 To $aFilter[0]
      Local $FilterItem = StringStripWS($aFilter[$iFCount], 3) ;Strip leading and trailing spaces
      If StringRegExp($FilterItem, "[\\/:<>|]") Then ContinueLoop ;Look for bad chars
      $hSearch = FileFindFirstFile($sPathItem & $FilterItem)
      If @error Then ContinueLoop
      Switch $TFlag
        Case 0 ;Files and Folders
          While True
            $sFile = FileFindNextFile($hSearch)
            If @error Then ExitLoop
            ;optional exclude filename/folder
        If $sTExclude Then
              If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
         EndIf
            $sFileList &= $sDelim & $sFile
          WEnd
        Case 1 ;Files Only
          While True
            $sFile = FileFindNextFile($hSearch)
            If @error Then ExitLoop
            ;If StringInStr(FileGetAttrib($sPath & $sFile), "D") <> 0 Then ContinueLoop; bypass folder (ver 3.3.0.0)
            If @extended Then ContinueLoop ;bypass folder (for Autoit versions > 3.3.0.0)
            ;optional exclude filename/folder
            If $sTExclude Then
               If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
         EndIf
            $sFileList &= $sDelim & $sFile
          WEnd
        Case 2 ;Folders Only
          While True
            $sFile = FileFindNextFile($hSearch)
            If @error Then ExitLoop
            ;If StringInStr(FileGetAttrib($sPath & $sFile), "D") = 0 Then ContinueLoop; bypass file (ver 3.3.0.0)
            If @extended = 0 Then ContinueLoop ;bypass file (for Autoit versions > 3.3.0.0)
            ;optional exclude filename/folder
            If $sTExclude Then
              If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
            EndIf
            $sFileList &= $sDelim & $sFile
          WEnd
        Case Else
          Return SetError(3, 3, "")
      EndSwitch
      FileClose($hSearch)
    Next

    ;---------------

    ;optional do a recursive search
    If $bRecursiv Then
      $hSearch = FileFindFirstFile($sPathItem & "*.*")
      If Not @error Then
        While True
          $sFile = FileFindNextFile($hSearch)
          If @error Then ExitLoop
          ;If StringInStr(FileGetAttrib($sPath & $sFile), "D") = 0 Then ContinueLoop; bypass file (ver 3.3.0.0)
          If @extended = 0 Then ContinueLoop ;bypass file (for Autoit versions > 3.3.0.0)
          ;optional exclude filename/folder
      If $sTExclude Then
        If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
      EndIf
      ;call recursive search
          $sFileList &= _FileListToArrayEx2($sPathItem & $sFile, $sFilter, $iFlag, $bRecursiv, $sExclude, 0)
        WEnd
        FileClose($hSearch)
      EndIf
    EndIf

  Next ;$iPCount

  ;---------------

  ;Set according return value
  Switch $iFormat
    Case 0
      Return $sFileList
    Case 1
      If $sFileList = "" Then
        Local $aRet[1] = [0]
        Return $aRet
      Else
        Return StringSplit(StringTrimLeft($sFileList, 1), "|", $iFormat)
      EndIf
    Case 2
      If $sFileList = "" Then
        Return ""
      Else
        Return StringSplit(StringTrimLeft($sFileList, 1), "|", $iFormat)
      EndIf
  EndSwitch

EndFunc   ;==>_FileListToArrayEx2
Edited by BaKaMu
Link to comment
Share on other sites

Really great ...

Just for my knowledge, I try to modified the original function to use @Extended. But even with this, it's not faster than the new one with so much function (I try it with $iFlag = 0 and $iFlag = 1 on 50000 files).

Your new function seems to be 2X faster.

If I compare original _FileListToArray() and a modified version that use @Extended (on v3.3.1.1) the work time of these 2 functions are similar. Just 150ms of gain with $iFlag = 0 but no gain with $iFlag = 1.

So we can said that this function is obsolete.

@BaKaMu

In the test of your new function, I noticed that if I want exclude a directory like $hs_mig$ (who is in my Windows dir), it doesn't work.

But once again, I'm really impressed by the speed of this function in spite of all its options.

Now, I am impatient of the release of the final version of the new version of AutoIt. :)

Best Regards.Thierry

Link to comment
Share on other sites

Dedicated to Speed-Freaks (like me): ;-)

Another speed optimized version.

A lot more code, but some more speed (compared to _FileListToArrayEx2 with the new DOS-Wildcard conversion).

@BaKaMu

In the test of your new function, I noticed that if I want exclude a directory like $hs_mig$ (who is in my Windows dir), it doesn't work.

Corrected in this version (i hope so, please test).

The DOS-Wildcard to RegExp conversion of $sExclude is now a little bit more complicated.

(maybe some experts for StringRegExpReplace have a better solution for this)

!!Edit-1!!: some time consuming requests removed (If $sTExclude ... ), gives some little speed gain if exclude is used.

!!Edit-2!!: better DOS-Wildcard to RegExp conversion, gives significant more speed if exclude is used, thanks KaFu and Ascend4nt.

New Version of _FileListToArrayEx

; #FUNCTION# ===========================================================================================
; Name:             _FileListToArrayEx3
; Description:      full compatible _FileListToArray replacement  (with more speed and additional features)
;                   additional: multi-path, multi-filter, multi exclude-filter, recursiv search
;                   optional full pathname
; Syntax:           _FileListToArrayEx3([$sPath = @ScriptDir, [$sFilter = "*", [$iFlag = 0, [$bRecursiv = False, [$sExclude = "", [$iFormat = 1]]]]]])
; Parameter(s):     $sPath = optional: path to generate filelist for, multi paths separated with semicolon (ex: "C:\Tmp;D:\Temp")
;                            if no path is given then @ScriptDir is used
;                   $sFilter = optional: The filter to use. (default: "*")
;                              multi filters separated with semicolon (ex: *.exe; *.txt will find all .exe and .txt files)
;                              (Search the Autoit3 manual for the word "WildCards" for details)
;                   $iFlag = Optional: specifies whether to return files, folders or both
;                            0 = (Default) Return both files and folders
;                            1 = Return files only
;                            2 = Return folders only
;                            $iFlag + 4 = Return Filenames and/or Folders incl full Path
;                   $bRecursiv = optional: true: recursive search in rootdir and subdirs
;                                          False (default): search only in rootdir
;                   $sExclude = optional: exclude a file/folder from the list by all or part of its name, various statements delimited with semicolon
;                               (ex: Unins* will remove all files/folders that start with Unins)
;                   $iFormat =  optional: return format
;                               0 = String ( "|" delimited)
;                               1 = (default) one-dimensional array, array[0] = number of files\folders returned
;                               2 = one-dimensional array, 0-based
; Requirement(s):   none
; Return Value(s):  on success: string or array (dependent on $iFormat)
; Author(s):        bernd670, Tlem, Spiff59, Zedna, KaFu, SmOke_N, GEOSoft, Ascend4nt, BaKaMu
; ====================================================================================================
Func _FileListToArrayEx3($sPath = @ScriptDir, $sFilter = "*", $iFlag = 0, $bRecursiv = False, $sExclude = "", $iFormat = 1)
  Local $hSearch, $iPCount, $iFCount, $sFile, $TFlag = 0, $sFileList = "", $sTExclude = ""

  If $sPath = -1 Or $sPath = Default Then $sPath = @ScriptDir
  If $sFilter = -1 Or $sFilter = Default Then $sFilter = "*"
  If $iFlag = -1 Or $iFlag = Default Then $iFlag = 0
  If $bRecursiv = Default Then $bRecursiv = False
  If $sExclude = -1 Or $sExclude = Default Then $sExclude = ""
  If $iFormat = -1 Or $iFormat = Default Then $iFormat = 1

  ;separate multi path
  Local $aPath = StringSplit($sPath, ';')
  ;separate multi filter
  Local $aFilter = StringSplit($sFilter, ';')

  If $sExclude Then

    ;prepare $sTExclude
    ;Strip leading and trailing spaces and spaces between semi colon delimiter
    $sTExclude = StringStripWS(StringRegExpReplace($sExclude, "\s*;\s*", ";"), 3)
    ;convert $sExclude to fit StringRegExp (not perfect but useable)
    $sTExclude = StringRegExpReplace($sTExclude, '([\Q\.+[^]$(){}=!\E])', '\\$1')  ;thanks KaFu and Ascend4nt
    $sTExclude = StringReplace($sTExclude, "?", ".")
    $sTExclude = StringReplace($sTExclude, "*", ".*?")
    $sTExclude = StringReplace($sTExclude, ";", "|")

    For $iPCount = 1 To $aPath[0]
      Local $sPathItem = StringStripWS($aPath[$iPCount], 3) ;Strip leading and trailing spaces
      Local $sDelim = "|" ;reset $sDelim

      If StringRight($sPathItem, 1) <> "\" Then $sPathItem = $sPathItem & "\" ;check for trailing "\"

      ;return full-path or filename only ($iFlag 0,1,2 = Filename, $iFlag 4,5,6 = full path)
      If $iFlag > 3 Then
        $sDelim &= $sPathItem
        $TFlag = $iFlag - 4
      Else
        $TFlag = $iFlag
      EndIf

      ;perform the search
      For $iFCount = 1 To $aFilter[0]
        Local $FilterItem = StringStripWS($aFilter[$iFCount], 3) ;Strip leading and trailing spaces
        If StringRegExp($FilterItem, "[\\/:<>|]") Then ContinueLoop ;Look for bad chars
        $hSearch = FileFindFirstFile($sPathItem & $FilterItem)
        If @error Then ContinueLoop
        Switch $TFlag
          Case 0 ;Files and Folders
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              ;check for exclude filename/folder
              If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
              $sFileList &= $sDelim & $sFile
            WEnd
          Case 1 ;Files Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              ;If StringInStr(FileGetAttrib($sPath & $sFile), "D") <> 0 Then ContinueLoop; bypass folder (ver 3.3.0.0)
              If @extended Then ContinueLoop ;bypass folder (for Autoit versions > 3.3.0.0)
              ;check for exclude filename/folder
              If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
              $sFileList &= $sDelim & $sFile
            WEnd
          Case 2 ;Folders Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              ;If StringInStr(FileGetAttrib($sPath & $sFile), "D") = 0 Then ContinueLoop; bypass file (ver 3.3.0.0)
              If @extended = 0 Then ContinueLoop ;bypass file (for Autoit versions > 3.3.0.0)
              ;check for exclude filename/folder
              If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
              $sFileList &= $sDelim & $sFile
            WEnd
          Case Else
            Return SetError(3, 3, "")
        EndSwitch
        FileClose($hSearch)
      Next

      ;---------------

      ;optional do a recursive search
      If $bRecursiv Then
        $hSearch = FileFindFirstFile($sPathItem & "*.*")
        If Not @error Then
          While True
            $sFile = FileFindNextFile($hSearch)
            If @error Then ExitLoop
            ;If StringInStr(FileGetAttrib($sPath & $sFile), "D") = 0 Then ContinueLoop; bypass file (ver 3.3.0.0)
            If @extended = 0 Then ContinueLoop ;bypass file (for Autoit versions > 3.3.0.0)
            ;check for exclude folder
            If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
            ;call recursive search
            $sFileList &= _FileListToArrayEx3($sPathItem & $sFile, $sFilter, $iFlag, $bRecursiv, $sExclude, 0)
          WEnd
          FileClose($hSearch)
        EndIf
      EndIf

    Next ;$iPCount

  Else ;If Not $sExclude

    For $iPCount = 1 To $aPath[0]
      Local $sPathItem = StringStripWS($aPath[$iPCount], 3) ;Strip leading and trailing spaces
      Local $sDelim = "|" ;reset $sDelim

      If StringRight($sPathItem, 1) <> "\" Then $sPathItem = $sPathItem & "\" ;check for trailing "\"

      ;return full-path or filename only ($iFlag 0,1,2 = Filename, $iFlag 4,5,6 = full path)
      If $iFlag > 3 Then
        $sDelim &= $sPathItem
        $TFlag = $iFlag - 4
      Else
        $TFlag = $iFlag
      EndIf

      ;perform the search
      For $iFCount = 1 To $aFilter[0]
        Local $FilterItem = StringStripWS($aFilter[$iFCount], 3) ;Strip leading and trailing spaces
        If StringRegExp($FilterItem, "[\\/:<>|]") Then ContinueLoop ;Look for bad chars
        $hSearch = FileFindFirstFile($sPathItem & $FilterItem)
        If @error Then ContinueLoop
        Switch $TFlag
          Case 0 ;Files and Folders
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              $sFileList &= $sDelim & $sFile
            WEnd
          Case 1 ;Files Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              ;If StringInStr(FileGetAttrib($sPath & $sFile), "D") <> 0 Then ContinueLoop; bypass folder (ver 3.3.0.0)
              If @extended Then ContinueLoop ;bypass folder (for Autoit versions > 3.3.0.0)
              $sFileList &= $sDelim & $sFile
            WEnd
          Case 2 ;Folders Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              ;If StringInStr(FileGetAttrib($sPath & $sFile), "D") = 0 Then ContinueLoop; bypass file (ver 3.3.0.0)
              If @extended = 0 Then ContinueLoop ;bypass file (for Autoit versions > 3.3.0.0)
              $sFileList &= $sDelim & $sFile
            WEnd
          Case Else
            Return SetError(3, 3, "")
        EndSwitch
        FileClose($hSearch)
      Next

      ;---------------

      ;optional do a recursive search
      If $bRecursiv Then
        $hSearch = FileFindFirstFile($sPathItem & "*.*")
        If Not @error Then
          While True
            $sFile = FileFindNextFile($hSearch)
            If @error Then ExitLoop
            ;If StringInStr(FileGetAttrib($sPath & $sFile), "D") = 0 Then ContinueLoop; bypass file (ver 3.3.0.0)
            If @extended = 0 Then ContinueLoop ;bypass file (for Autoit versions > 3.3.0.0)
            ;call recursive search
            $sFileList &= _FileListToArrayEx3($sPathItem & $sFile, $sFilter, $iFlag, $bRecursiv, $sExclude, 0)
          WEnd
          FileClose($hSearch)
        EndIf
      EndIf

    Next ;$iPCount

  EndIf ;If $sExclude

  ;---------------

  ;Set according return value
  Switch $iFormat
    Case 0
      Return $sFileList
    Case 1
      If $sFileList = "" Then
        Local $aRet[1] = [0]
        Return $aRet
      Else
        Return StringSplit(StringTrimLeft($sFileList, 1), "|", $iFormat)
      EndIf
    Case 2
      If $sFileList = "" Then
        Return ""
      Else
        Return StringSplit(StringTrimLeft($sFileList, 1), "|", $iFormat)
      EndIf
  EndSwitch

EndFunc   ;==>_FileListToArrayEx3
Edited by BaKaMu
Link to comment
Share on other sites

I think the debate is more simplicity versus complexity.

I've nothing against complexity, if it is useful, but all the new parameters open up a lot of combinations of interaction between $iFlag, $sFilter, and any specified exclude strings. I mentioned a few of the possibilities in my last post. I would think that how these parms all work together would need to be determined, and documented.

I don't know what are plans of Autoit's developers but maybe _FileListToArray() can remain as it is (only optimized without new parametres)

and new robust version _FileListToArrayEx() ca be added with all possible functionality like excludes etc.

Edited by Zedna
Link to comment
Share on other sites

For me as soon the extended version is ready without breaking the previous release/beta version I will incorporate it in the standard UDF please uppdate the doc at the same time :):);)

Just PM me the function an its doc

Link to comment
Share on other sites

I don't know what are plans of Autoit's developers but maybe _FileListToArray() can remain as it is (only optimized without new parametres)

and new robust version _FileListToArrayEx() ca be added with all possible functionality like excludes etc.

Two versions, FileListToArray() and FileListToArrayEx() sounds like a good idea to me.

If the Ex version is to have all the bells-and-whistles, then there's something missing, in my opinion.

Like a DOS search, if you start from a certain directory and include subdirectories it will return partial paths, or referential paths, or relative paths, whatever it should be called. For instance, if you search the Autoit folder for *.exe, with recursion on, one of the elements returned would be "Aut2Exe\Aut2.exe". The path relative to $sPath.

Implementing it took only a few lines of code, but brought up another concern. Variable names are supposed to be descriptive, self-documenting, $iFLag doesn't pass that test. It had become a dual-use flag, setting both "what is being searched" and "the format of what is returned". I don't really care what names are settled upon, but in the version below (based on the latest post #66), I've split $iFlag into 2 variables:

$SrchType = What to search for: 0 = Files and Folder, 1 = Files Only, 2 = Folders Only

$PathType = The format of returned elements: 0 = file/folder name only, 1 = relative path, 2 = full path

What do you think?

#include<array.au3>

;Testing parameters
Dim $Repeat = 1
Dim $SearchPath = "C:\Program Files\Autoit3\AutoitX"
Dim $Pattern = "*"
Dim $SrchType = 1; What to search for: 0 = Files and Folders, 1 = Files Only, 2 = Folders Only
Dim $PathType = 1; Format of returned string: 0 = file/folder name only, 1 = partial/referential path, 2 = full path
Dim $Exclude = ""
Dim $Recursiv = True

  $aRet = _FileListToArrayEx3($SearchPath, $Pattern, $SrchType, $PathType, $Recursiv, $Exclude, 1)
  _ArrayDisplay($aRet)
Exit

; #FUNCTION# ===========================================================================================
; Name:          _FileListToArrayEx3
; Description:    full compatible _FileListToArray replacement  (with more speed and additional features)
;                  additional: multi-path, multi-filter, multi exclude-filter, recursiv search
;                  optional full pathname
; Syntax:          _FileListToArrayEx3([$sPath = @ScriptDir, [$sFilter = "*", [$iSrchType, [$bRecursiv = False, [$sExclude = "", [$iFormat = 1]]]]]])
; Parameter(s):  $sPath = optional: path to generate filelist for, multi paths separated with semicolon (ex: "C:\Tmp;D:\Temp")
;                           if no path is given then @ScriptDir is used
;                  $sFilter = optional: The filter to use. (default: "*")
;                             multi filters separated with semicolon (ex: *.exe; *.txt will find all .exe and .txt files)
;                             (Search the Autoit3 manual for the word "WildCards" for details)
;                  $iSrchType = Optional: specifies whether to return files, folders or both
;                           0 = (Default) Return both files and folders
;                           1 = Return files only
;                           2 = Return folders only
;                           $iSrchType + 4 = Return Filenames and/or Folders incl full Path
;                  $bRecursiv = optional: true: recursive search in rootdir and subdirs
;                                         False (default): search only in rootdir
;                  $sExclude = optional: exclude a file/folder from the list by all or part of its name, various statements delimited with semicolon
;                              (ex: Unins* will remove all files/folders that start with Unins)
;                  $iFormat =  optional: return format
;                              0 = String ( "|" delimited)
;                              1 = (default) one-dimensional array, array[0] = number of files\folders returned
;                              2 = one-dimensional array, 0-based
; Requirement(s):   none
; Return Value(s):  on success: string or array (dependent on $iFormat)
; Author(s):        bernd670, Tlem, Spiff59, Zedna, KaFu, SmOke_N, GEOSoft, Ascend4nt, BaKaMu
; ====================================================================================================




Func _FileListToArrayEx3($sPath = @ScriptDir, $sFilter = "*", $iSrchType = 0, $iPathType = 0, $bRecursiv = False, $sExclude = "", $iFormat = 1, $wrk = "")
  Local $hSearch, $iPCount, $iFCount, $sFile, $SrchType = 0, $sFileList = "", $sTExclude = "", $wrk2

  If $sPath = -1 Or $sPath = Default Then $sPath = @ScriptDir
  If $sFilter = -1 Or $sFilter = Default Then $sFilter = "*"
  If $iSrchType = -1 Or $iSrchType = Default Then $iSrchType = 0
  If $bRecursiv = Default Then $bRecursiv = False
  If $sExclude = -1 Or $sExclude = Default Then $sExclude = ""
  If $iFormat = -1 Or $iFormat = Default Then $iFormat = 1

;separate multi path
  Local $aPath = StringSplit($sPath, ';')
;separate multi filter
  Local $aFilter = StringSplit($sFilter, ';')

  If $sExclude Then

  ;prepare $sTExclude
  ;Strip leading and trailing spaces and spaces between semi colon delimiter
    $sTExclude = StringStripWS(StringRegExpReplace($sExclude, "\s*;\s*", ";"), 3)
  ;convert $sExclude to fit StringRegExp (not perfect but useable)
    $sTExclude = StringRegExpReplace($sTExclude, '([\Q\.+[^]$(){}=!\E])', '\\$1');thanks KaFu and Ascend4nt
    $sTExclude = StringReplace($sTExclude, "?", ".")
    $sTExclude = StringReplace($sTExclude, "*", ".*?")
    $sTExclude = StringReplace($sTExclude, ";", "|")

    For $iPCount = 1 To $aPath[0]
      Local $sPathItem = StringStripWS($aPath[$iPCount], 3);Strip leading and trailing spaces
      Local $sDelim = "|";reset $sDelim

      If StringRight($sPathItem, 1) <> "\" Then $sPathItem = $sPathItem & "\";check for trailing "\"

    ;return full-path
      If $iPathType = 2 Then $sDelim &= $sPathItem

    ;perform the search
      For $iFCount = 1 To $aFilter[0]
        Local $FilterItem = StringStripWS($aFilter[$iFCount], 3);Strip leading and trailing spaces
        If StringRegExp($FilterItem, "[\\/:<>|]") Then ContinueLoop;Look for bad chars
        $hSearch = FileFindFirstFile($sPathItem & $FilterItem)
        If @error Then ContinueLoop
        Switch $SrchType
          Case 0;Files and Folders
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
            ;check for exclude filename/folder
              If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
              $sFileList &= $sDelim & $wrk & $sFile
            WEnd
          Case 1;Files Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              If @extended Then ContinueLoop;bypass folder (for Autoit versions > 3.3.0.0)
            ;check for exclude filename/folder
              If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
              $sFileList &= $sDelim & $wrk & $sFile
            WEnd
          Case 2;Folders Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              If @extended = 0 Then ContinueLoop;bypass file (for Autoit versions > 3.3.0.0)
            ;check for exclude filename/folder
              If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
              $sFileList &= $sDelim & $wrk & $sFile
            WEnd
          Case Else
            Return SetError(3, 3, "")
        EndSwitch
        FileClose($hSearch)
      Next

    ;---------------

    ;optional do a recursive search
      If $bRecursiv Then
        $hSearch = FileFindFirstFile($sPathItem & "*.*")
        If Not @error Then
          While True
            $sFile = FileFindNextFile($hSearch)
            If @error Then ExitLoop
            If @extended = 0 Then ContinueLoop;bypass file (for Autoit versions > 3.3.0.0)
          ;check for exclude folder
            If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
          ;call recursive search
            $sFileList &= _FileListToArrayEx3($sPathItem & $sFile, $sFilter, $iSrchType, $bRecursiv, $sExclude, 0)
          WEnd
          FileClose($hSearch)
        EndIf
      EndIf

    Next;$iPCount

  Else;If Not $sExclude

    For $iPCount = 1 To $aPath[0]
      Local $sPathItem = StringStripWS($aPath[$iPCount], 3);Strip leading and trailing spaces
      Local $sDelim = "|";reset $sDelim

      If StringRight($sPathItem, 1) <> "\" Then $sPathItem = $sPathItem & "\";check for trailing "\"

    ;return full-path
      If $iPathType = 2 Then $sDelim &= $sPathItem

    ;perform the search
      For $iFCount = 1 To $aFilter[0]
        Local $FilterItem = StringStripWS($aFilter[$iFCount], 3);Strip leading and trailing spaces
        If StringRegExp($FilterItem, "[\\/:<>|]") Then ContinueLoop;Look for bad chars
        $hSearch = FileFindFirstFile($sPathItem & $FilterItem)
        If @error Then ContinueLoop
        Switch $SrchType
          Case 0;Files and Folders
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              $sFileList &= $sDelim & $wrk & $sFile
            WEnd
          Case 1;Files Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              If @extended Then ContinueLoop;bypass folder (for Autoit versions > 3.3.0.0)
              $sFileList &= $sDelim & $wrk & $sFile
            WEnd
          Case 2;Folders Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              If @extended = 0 Then ContinueLoop;bypass file (for Autoit versions > 3.3.0.0)
              $sFileList &= $sDelim & $wrk & $sFile
            WEnd
          Case Else
            Return SetError(3, 3, "")
        EndSwitch
        FileClose($hSearch)
      Next

    ;---------------

    ;optional do a recursive search
      If $bRecursiv Then
        $hSearch = FileFindFirstFile($sPathItem & "*.*")
        If Not @error Then
          While True
            $sFile = FileFindNextFile($hSearch)
            If @error Then ExitLoop
            If @extended = 0 Then ContinueLoop;bypass file (for Autoit versions > 3.3.0.0)
          ;call recursive search
            If $PathType = 1 Then $wrk2 = $wrk & $sFile & "\"
            $sFileList &= _FileListToArrayEx3($sPathItem & $sFile, $sFilter, $iSrchType, $iPathType, $bRecursiv, $sExclude, 0, $wrk2)
          WEnd
          FileClose($hSearch)
        EndIf
      EndIf

    Next;$iPCount

  EndIf;If $sExclude

;---------------

;Set according return value
  Switch $iFormat
    Case 0
      Return $sFileList
    Case 1
      If $sFileList = "" Then
        Local $aRet[1] = [0]
        Return $aRet
      Else
        Return StringSplit(StringTrimLeft($sFileList, 1), "|", $iFormat)
      EndIf
    Case 2
      If $sFileList = "" Then
        Return ""
      Else
        Return StringSplit(StringTrimLeft($sFileList, 1), "|", $iFormat)
      EndIf
  EndSwitch

EndFunc ;==>_FileListToArrayEx3
Edited by Spiff59
Link to comment
Share on other sites

If the Ex version is to have all the bells-and-whistles, then there's something missing, in my opinion.

Like a DOS search, if you start from a certain directory and include subdirectories it will return partial paths, or referential paths, or relative paths, whatever it should be called. For instance, if you search the Autoit folder for *.exe, with recursion on, one of the elements returned would be "Aut2Exe\Aut2.exe". The path relative to $sPath.

What do you think?

To spend a extra flag for the type of returned path (none, relative, full) is a additional feature for _FileListToArrayEx.

(even though i don't know a really useful application for relative pathes, but maybe good for others)

There have been some bugs in your implementation, so i have corrected this.

(and have made some clarification for variable names)

Your implementation also decreases the speed a little bit (but not noticeable)

Most important is the fact, that the first 3 variables ($sPath, $sFilter, $iSearchType) are remaining the same,

so the compatibility to _FileListToArray is given.

So it maybe advisable to name $iSearchType back to $iFlag, cause in _FileListToArray it is called $iFlag

(to not confuse same user, but this is discussible)

The corrected code of your implementation.

; #FUNCTION# ===========================================================================================
; Name:             _FileListToArrayEx4
; Description:      full compatible _FileListToArray replacement  (with more speed and additional features)
;                   additional: multi-path, multi-filter, multi exclude-filter, recursiv search
;                   optional full pathname
; Syntax:           _FileListToArrayEx4([$sPath = @ScriptDir, [$sFilter = "*", [$iSearchType, [$bRecursiv = False, [$sExclude = "", [$iRetFormat = 1]]]]]])
; Parameter(s):     $sPath = optional: path to generate filelist for, multi paths separated with semicolon (ex: "C:\Tmp;D:\Temp")
;                            if no path is given then @ScriptDir is used
;                   $sFilter = optional: The filter to use. (default: "*")
;                              multi filters separated with semicolon (ex: *.exe; *.txt will find all .exe and .txt files)
;                              (Search the Autoit3 manual for the word "WildCards" for details)
;                   $iSearchType = What to search for: 0 = Files and Folder, 1 = Files Only, 2 = Folders Only
;                   $iPathType = The format of returned elements: 0 = file/folder name only, 1 = relative path, 2 = full path
;                   $bRecursiv = optional: true: recursive search in rootdir and subdirs
;                                          False (default): search only in rootdir
;                   $sExclude = optional: exclude a file/folder from the list by all or part of its name, various statements delimited with semicolon
;                               (ex: Unins* will remove all files/folders that start with Unins)
;                   $iRetFormat =  optional: return format
;                               0 = String ( "|" delimited)
;                               1 = (default) one-dimensional array, array[0] = number of files\folders returned
;                               2 = one-dimensional array, 0-based
;                   $sWorkPath =      internal use only (do not assign a value)
; Requirement(s):   none
; Return Value(s):  on success: string or array (dependent on $iRetFormat)
; Author(s):        bernd670, Tlem, Spiff59, Zedna, KaFu, SmOke_N, GEOSoft, Ascend4nt, BaKaMu
; ====================================================================================================
Func _FileListToArrayEx4($sPath = @ScriptDir, $sFilter = "*", $iSearchType = 0, $iPathType = 0, $bRecursiv = False, $sExclude = "", $iRetFormat = 1, $sWorkPath = "")
  Local $hSearch, $iPCount, $iFCount, $sFile, $sFileList = "", $sTExclude = "", $sTWorkPath

  If $sPath = -1 Or $sPath = Default Then $sPath = @ScriptDir
  If $sFilter = -1 Or $sFilter = Default Then $sFilter = "*"
  If $iSearchType = -1 Or $iSearchType = Default Then $iSearchType = 0
  If $iPathType = -1 Or $iPathType = Default Then $iPathType = 0
  If $bRecursiv = Default Then $bRecursiv = False
  If $sExclude = -1 Or $sExclude = Default Then $sExclude = ""
  If $iRetFormat = -1 Or $iRetFormat = Default Then $iRetFormat = 1

  ;separate multi path
  Local $aPath = StringSplit($sPath, ';')
  ;separate multi filter
  Local $aFilter = StringSplit($sFilter, ';')

  If $sExclude Then

    ;prepare $sTExclude
    ;Strip leading and trailing spaces and spaces between semi colon delimiter
    $sTExclude = StringStripWS(StringRegExpReplace($sExclude, "\s*;\s*", ";"), 3)
    ;convert $sExclude to fit StringRegExp (not perfect but useable)
    $sTExclude = StringRegExpReplace($sTExclude, '([\Q\.+[^]$(){}=!\E])', '\\$1') ;thanks KaFu and Ascend4nt
    $sTExclude = StringReplace($sTExclude, "?", ".")
    $sTExclude = StringReplace($sTExclude, "*", ".*?")
    $sTExclude = StringReplace($sTExclude, ";", "|")

    For $iPCount = 1 To $aPath[0]
      Local $sPathItem = StringStripWS($aPath[$iPCount], 3) ;Strip leading and trailing spaces
      Local $sDelim = "|" ;reset $sDelim

      If StringRight($sPathItem, 1) <> "\" Then $sPathItem = $sPathItem & "\" ;check for trailing "\"

      ;return full-path
      If $iPathType = 2 Then $sDelim &= $sPathItem

      ;perform the search
      For $iFCount = 1 To $aFilter[0]
        Local $FilterItem = StringStripWS($aFilter[$iFCount], 3) ;Strip leading and trailing spaces
        If StringRegExp($FilterItem, "[\\/:<>|]") Then ContinueLoop ;Look for bad chars
        $hSearch = FileFindFirstFile($sPathItem & $FilterItem)
        If @error Then ContinueLoop
        Switch $iSearchType
          Case 0 ;Files and Folders
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              ;check for exclude filename/folder
              If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
              $sFileList &= $sDelim & $sWorkPath & $sFile
            WEnd
          Case 1 ;Files Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              If @extended Then ContinueLoop ;bypass folder (for Autoit versions > 3.3.0.0)
              ;check for exclude filename/folder
              If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
              $sFileList &= $sDelim & $sWorkPath & $sFile
            WEnd
          Case 2 ;Folders Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              If @extended = 0 Then ContinueLoop ;bypass file (for Autoit versions > 3.3.0.0)
              ;check for exclude filename/folder
              If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
              $sFileList &= $sDelim & $sWorkPath & $sFile
            WEnd
          Case Else
            Return SetError(3, 3, "")
        EndSwitch
        FileClose($hSearch)
      Next

      ;---------------

      ;optional do a recursive search
      If $bRecursiv Then
        $hSearch = FileFindFirstFile($sPathItem & "*.*")
        If Not @error Then
          While True
            $sFile = FileFindNextFile($hSearch)
            If @error Then ExitLoop
            If @extended = 0 Then ContinueLoop ;bypass file (for Autoit versions > 3.3.0.0)
            ;check for exclude folder
            If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
            ;call recursive search
            If $iPathType = 1 Then $sTWorkPath = $sWorkPath & $sFile & "\"
            $sFileList &= _FileListToArrayEx4($sPathItem & $sFile, $sFilter, $iSearchType, $iPathType, $bRecursiv, $sExclude, 0, $sTWorkPath)
          WEnd
          FileClose($hSearch)
        EndIf
      EndIf

    Next ;$iPCount

  Else ;If Not $sExclude

    For $iPCount = 1 To $aPath[0]
      Local $sPathItem = StringStripWS($aPath[$iPCount], 3) ;Strip leading and trailing spaces
      Local $sDelim = "|" ;reset $sDelim

      If StringRight($sPathItem, 1) <> "\" Then $sPathItem = $sPathItem & "\" ;check for trailing "\"

      ;return full-path
      If $iPathType = 2 Then $sDelim &= $sPathItem

      ;perform the search
      For $iFCount = 1 To $aFilter[0]
        Local $FilterItem = StringStripWS($aFilter[$iFCount], 3) ;Strip leading and trailing spaces
        If StringRegExp($FilterItem, "[\\/:<>|]") Then ContinueLoop ;Look for bad chars
        $hSearch = FileFindFirstFile($sPathItem & $FilterItem)
        If @error Then ContinueLoop
        Switch $iSearchType
          Case 0 ;Files and Folders
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              $sFileList &= $sDelim & $sWorkPath & $sFile
            WEnd
          Case 1 ;Files Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              If @extended Then ContinueLoop ;bypass folder (for Autoit versions > 3.3.0.0)
              $sFileList &= $sDelim & $sWorkPath & $sFile
            WEnd
          Case 2 ;Folders Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              If @extended = 0 Then ContinueLoop ;bypass file (for Autoit versions > 3.3.0.0)
              $sFileList &= $sDelim & $sWorkPath & $sFile
            WEnd
          Case Else
            Return SetError(3, 3, "")
        EndSwitch
        FileClose($hSearch)
      Next

    ;---------------

      ;optional do a recursive search
      If $bRecursiv Then
        $hSearch = FileFindFirstFile($sPathItem & "*.*")
        If Not @error Then
          While True
            $sFile = FileFindNextFile($hSearch)
            If @error Then ExitLoop
            If @extended = 0 Then ContinueLoop ;bypass file (for Autoit versions > 3.3.0.0)
            ;call recursive search
            If $iPathType = 1 Then $sTWorkPath = $sWorkPath & $sFile & "\"
            $sFileList &= _FileListToArrayEx4($sPathItem & $sFile, $sFilter, $iSearchType, $iPathType, $bRecursiv, $sExclude, 0, $sTWorkPath)
          WEnd
          FileClose($hSearch)
        EndIf
      EndIf

    Next ;$iPCount

  EndIf ;If $sExclude

;---------------

;Set according return value
  Switch $iRetFormat
    Case 0
      Return $sFileList
    Case 1
      If $sFileList = "" Then
        Local $aRet[1] = [0]
        Return $aRet
      Else
        Return StringSplit(StringTrimLeft($sFileList, 1), "|", $iRetFormat)
      EndIf
    Case 2
      If $sFileList = "" Then
        Return ""
      Else
        Return StringSplit(StringTrimLeft($sFileList, 1), "|", $iRetFormat)
      EndIf
  EndSwitch

EndFunc ;==>_FileListToArrayEx4
Edited by BaKaMu
Link to comment
Share on other sites

There have been some bugs in your implementation,

It looks the same to me... just curious... what were the bugs?

Since I was just floating the idea, I hadn't bothered to update the description in the function header.

If we're going to submit both a new FileListToArray and a FileListToArryaEx together, then having the internal parameter names match is not a problem, and changing them wouldn't be a script-breaker. It's nitpicking really, $iFlag has no useful meaning as far as self-documentation, but if it stays with that name I wouldn't lose sleep any over it.

Edit: Oh, I'd forgotten, there are two of everything now. I had only updated one of the two recursion loops.

Edited by Spiff59
Link to comment
Share on other sites

It looks the same to me... just curious... what were the bugs?

Since I was just floating the idea, I hadn't bothered to update the description in the function header.

If we're going to submit both a new FileListToArray and a FileListToArryaEx together, then having the internal parameter names match is not a problem, and changing them wouldn't be a script-breaker. It's nitpicking really, $iFlag has no useful meaning as far as self-documentation, but if it stays with that name I wouldn't lose sleep any over it.

Edit: Oh, I'd forgotten, there are two of everything now. I had only updated one of the two recursion loops.

this bug you have seen,

and you have $PathType in line 194 (should be $iPathType),

and you have $SrchType in line 85, 156 (should be $iSrchType)

and then you can remove $SrchType from local declaration

You got no error, cause these variables are declared outside of function (perfidiously error) :-)

Edited by BaKaMu
Link to comment
Share on other sites

and you have $PathType in line 194 (should be $iPathType),

and you have $SrchType in line 85, 156 (should be $iSrchType)

Ah, right you are. I trashed some stuff doing global replaces and then manually patched up the variable names. I wasn't very thorough :)

So... Error handling and the documentation about "Return value(s)" isn't finished.

The exisiting FileListToArray() claims to return @error = 4 for a not-find, but actually only works if $iFlag = 0, otherwise it is returning a one-element array with that element empty. There is a 1-line fix for that to make it return @error = 4 in all cases.

In the new routine, the option for multiple pathnames and multiple filters negates the possibility of the @error = 1 and @error = 2 returns of the original routine. @error = 4 is also gone, and depending on the $iFormat flag either a null string, or a one-based array with a value of zero, is returned. Maybe the last @error should be trashed, the one for invalid $iFlag, just make the code "Case 1: Files only, Case 2: Folders only, Case Else: Files and Folders"?

This handling of @error is a script-breaker and if there is an intention of this replacing, or being baclward-compatible with the exisiting FileListToArray() there's a problem.

But this version does everything but butter your toast, and is still faster than the original. Do we want to (request to) retain FileListToArray() using the new fast basic version with the @error handling intact? And then have the FileListToArrayEx super-function that has different error processing?

typo

Edited by Spiff59
Link to comment
Share on other sites

To my opinion, FileListToArrayEx() is a new function. The original function must has to remain such which.

But it can be improved by few modification like the use of @Extended and my first suggestion to not use the array in the function but strings, and the return of the full path.

The line for Windows 98 is not any more right to be because AutoIt does not support any more Win98

So I suggest the optimized version by Spiff59. This version is really more faster than the original (2X), but it's the same. :)

Func _FileListToArray($sPath, $sFilter = "*", $iFlag = 0)
    Local $hSearch, $sFile, $sFileList, $sDelim = "|"

    If Not FileExists($sPath) Then Return SetError(1, 1, "")
    If StringRegexp($sFilter, "[\\/:<>|]") Or (Not StringStripWS($sFilter, 8)) Then Return SetError(2, 2, "")
    If StringRight($sPath, 1) <> "\" Then $sPath &= "\" 

    $hSearch = FileFindFirstFile($sPath & $sFilter)
    If $hSearch = -1 Then Return SetError(4, 4, "")

    If $iFlag > 3 Then
        $sDelim &= $sPath
        $iflag -= 4
    EndIf

    Switch $iFlag
        Case 0; Files and Folders
            While 1
                $sFile = FileFindNextFile($hSearch)
                If @error Then ExitLoop
                $sFileList &= $sDelim & $sFile
            WEnd
        Case 1; Files Only
            While 1
                $sFile = FileFindNextFile($hSearch)
                If @error Then ExitLoop
                If @extended Then ContinueLoop; bypass folder
                $sFileList &= $sDelim & $sFile
            WEnd
            If Not $sFileList Then Return SetError(4, 4, "")
        Case 2; Folders Only
            While 1
                $sFile = FileFindNextFile($hSearch)
                If @error Then ExitLoop
                If @extended = 0 Then ContinueLoop; bypass file
                $sFileList &= $sDelim & $sFile
            WEnd
            If Not $sFileList Then Return SetError(4, 4, "")
        Case Else
            Return SetError(3, 3, "")
    EndSwitch

    FileClose($hSearch)
    Return StringSplit(StringTrimLeft($sFileList, 1), "|")
EndFunc;==>_FileListToArray

What do you think about that jpm ?

Edited by Tlem

Best Regards.Thierry

Link to comment
Share on other sites

The line with the "Win98" reference removed a trailing slash, but then a later line added it back. I think it's preferential that the slash be present? And the bug regarding not returning @error = 4 when no files/folders are found should be addressed. I think the 3 extra lines (1 for the slash, 2 for the @error bug) in the example in post #63 are desirable?

Link to comment
Share on other sites

So... Error handling and the documentation about "Return value(s)" isn't finished.

The exisiting FileListToArray() claims to return @error = 4 for a not-find, but actually only works if $iFlag = 0, otherwise it is returning a one-element array with that element empty. There is a 1-line fix for that to make it return @error = 4 in all cases.

In the new routine, the option for multiple pathnames and multiple filters negates the possibility of the @error = 1 and @error = 2 returns of the original routine. @error = 4 is also gone, and depending on the $iFormat flag either a null string, or a one-based array with a value of zero, is returned. Maybe the last @error should be trashed, the one for invalid $iFlag, just make the code "Case 1: Files only, Case 2: Folders only, Case Else: Files and Folders"?

This handling of @error is a script-breaker and if there is an intention of this replacing, or being backward-compatible with the exisiting FileListToArray() there's a problem.

Some thoughts about return values for _FileListToArrayEx:

Return values depends on $Format or $RetFormat (newest version) flag

Case 0 : if something found returns delimited string

if nothing found returns empty string

Case 1 : if something found returns one-dimensional array, array[0] = number of files\folders returned

if nothing found returns one-dimensional array, array[0] = 0

Case 2 : if something found returns one-dimensional array, 0-based

if nothing found returns one-dimensional array, 0-based with 0 elements, changed now to $aRet[0] (is this practical ??)

----------

Some thought about error handling for _FileListToArrayEx:

My opinion is that an error should occur if some parameters are invalid.

In all other situation: garbage in -> no result

Version _FileListToArrayEx3:

- $sPath: If a path don't exists there should be an error.

But if there are multi path then there might be the situation that the first path don't exist and the second one exists

Is this now an error (and on error you have to stop the routine) or not?

I for me decided that this situation is not an error, simply there is no result for the non existing path.

(so i removed the error handling for this situation)

- $sFilter: same deliberations as for $sPath

- $iFlag : if $iFlag <> 0,1,2 4,5,6 there should be an error , Return SetError(3, 3, "")

- $bRecursiv: there could be nothing wrong, either it's true or not

- $sExclude: i don't see any situation where an problem could occur (maybe i am wrong)

- $iRetFormat: if $iRetFormat <> 0,1,2 there should be an error , i have now implemented Return SetError(4, 4, "")

Version _FileListToArrayEx4:

- $SearchType: if $SearchType <> 0,1,2 there should be an error , Return SetError(3, 3, "")

- $PathType: if $PathType <> 0,1,2 there should be an error , Return SetError(3, 3, "") (should be implemented)

- $sWorkPath: only internal use, no error checking necessary

So what do you think about that

(Uh, that was the longest text i ever wrote in this forum) :-)

Improved Version of _FileListToArray3

; #FUNCTION# ===========================================================================================
; Name:             _FileListToArrayEx3
; Description:      full compatible _FileListToArray replacement  (with more speed and additional features)
;                   additional: multi-path, multi-filter, multi exclude-filter, recursive search
;                   optional full pathname
; Syntax:           _FileListToArrayEx3([$sPath = @ScriptDir, [$sFilter = "*", [$iFlag = 0, [$bRecursiv = False, [$sExclude = "", [$iRetFormat = 1]]]]]])
; Parameter(s):     $sPath = optional: path to generate filelist for, multi paths separated with semicolon (ex: "C:\Tmp;D:\Temp")
;                            if no path is given then @ScriptDir is used
;                   $sFilter = optional: The filter to use. (default: "*")
;                              multi filters separated with semicolon (ex: *.exe; *.txt will find all .exe and .txt files)
;                              (Search the Autoit3 manual for the word "WildCards" for details)
;                   $iFlag = Optional: specifies whether to return files, folders or both
;                            0 = (Default) Return both files and folders
;                            1 = Return files only
;                            2 = Return folders only
;                            $iFlag + 4 = Return Filenames and/or Folders incl full Path
;                   $bRecursiv = optional: true: recursive search in rootdir and subdirs
;                                          False (default): search only in rootdir
;                   $sExclude = optional: exclude a file/folder from the list by all or part of its name, various statements delimited with semicolon
;                               (ex: Unins* will remove all files/folders that start with Unins)
;                   $iRetFormat =  optional: return format
;                               0 = String ( "|" delimited)
;                               1 = (default) one-dimensional array, array[0] = number of files\folders returned
;                               2 = one-dimensional array, 0-based
; Requirement(s):   none
; Return Value(s):  on success: string or array (dependent on $iRetFormat)
; Author(s):        bernd670, Tlem, Spiff59, Zedna, KaFu, SmOke_N, GEOSoft, Ascend4nt, BaKaMu
; ====================================================================================================
Func _FileListToArrayEx3($sPath = @ScriptDir, $sFilter = "*", $iFlag = 0, $bRecursiv = False, $sExclude = "", $iRetFormat = 1)
  Local $hSearch, $iPCount, $iFCount, $sFile, $TFlag = 0, $sFileList = "", $sTExclude = ""

  If $sPath = -1 Or $sPath = Default Then $sPath = @ScriptDir
  If $sFilter = -1 Or $sFilter = Default Then $sFilter = "*"
  If $iFlag = -1 Or $iFlag = Default Then $iFlag = 0
  If $bRecursiv = Default Then $bRecursiv = False
  If $sExclude = -1 Or $sExclude = Default Then $sExclude = ""
  If $iRetFormat = -1 Or $iRetFormat = Default Then $iRetFormat = 1

  ;separate multi path
  Local $aPath = StringSplit($sPath, ';')
  ;separate multi filter
  Local $aFilter = StringSplit($sFilter, ';')

  If $sExclude Then

    ;prepare $sTExclude
    ;Strip leading and trailing spaces and spaces between semi colon delimiter
    $sTExclude = StringStripWS(StringRegExpReplace($sExclude, "\s*;\s*", ";"), 3)
    ;convert $sExclude to fit StringRegExp (not perfect but useable)
    $sTExclude = StringRegExpReplace($sTExclude, '([\Q\.+[^]$(){}=!\E])', '\\$1') ;thanks KaFu and Ascend4nt
    $sTExclude = StringReplace($sTExclude, "?", ".")
    $sTExclude = StringReplace($sTExclude, "*", ".*?")
    $sTExclude = StringReplace($sTExclude, ";", "|")

    For $iPCount = 1 To $aPath[0]
      Local $sPathItem = StringStripWS($aPath[$iPCount], 3) ;Strip leading and trailing spaces
      Local $sDelim = "|" ;reset $sDelim

      If StringRight($sPathItem, 1) <> "\" Then $sPathItem = $sPathItem & "\" ;check for trailing "\"

      ;return full-path or filename only ($iFlag 0,1,2 = Filename, $iFlag 4,5,6 = full path)
      If $iFlag > 3 Then
        $sDelim &= $sPathItem
        $TFlag = $iFlag - 4
      Else
        $TFlag = $iFlag
      EndIf

      ;perform the search
      For $iFCount = 1 To $aFilter[0]
        Local $FilterItem = StringStripWS($aFilter[$iFCount], 3) ;Strip leading and trailing spaces
        If StringRegExp($FilterItem, "[\\/:<>|]") Then ContinueLoop ;Look for bad chars
        $hSearch = FileFindFirstFile($sPathItem & $FilterItem)
        If @error Then ContinueLoop
        Switch $TFlag
          Case 0 ;Files and Folders
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              ;check for exclude filename/folder
              If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
              $sFileList &= $sDelim & $sFile
            WEnd
          Case 1 ;Files Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              ;If StringInStr(FileGetAttrib($sPath & $sFile), "D") <> 0 Then ContinueLoop; bypass folder (ver 3.3.0.0)
              If @extended Then ContinueLoop ;bypass folder (for Autoit versions > 3.3.0.0)
              ;check for exclude filename/folder
              If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
              $sFileList &= $sDelim & $sFile
            WEnd
          Case 2 ;Folders Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              ;If StringInStr(FileGetAttrib($sPath & $sFile), "D") = 0 Then ContinueLoop; bypass file (ver 3.3.0.0)
              If @extended = 0 Then ContinueLoop ;bypass file (for Autoit versions > 3.3.0.0)
              ;check for exclude filename/folder
              If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
              $sFileList &= $sDelim & $sFile
            WEnd
          Case Else
            Return SetError(3, 3, "")
        EndSwitch
        FileClose($hSearch)
      Next

      ;---------------

      ;optional do a recursive search
      If $bRecursiv Then
        $hSearch = FileFindFirstFile($sPathItem & "*.*")
        If Not @error Then
          While True
            $sFile = FileFindNextFile($hSearch)
            If @error Then ExitLoop
            ;If StringInStr(FileGetAttrib($sPath & $sFile), "D") = 0 Then ContinueLoop; bypass file (ver 3.3.0.0)
            If @extended = 0 Then ContinueLoop ;bypass file (for Autoit versions > 3.3.0.0)
            ;check for exclude folder
            If StringRegExp(StringRegExpReplace($sFile, "(.*?[\\/]+)*(.*?\z)", "\2"), "(?i)\A" & $sTExclude & "\z") Then ContinueLoop
            ;call recursive search
            $sFileList &= _FileListToArrayEx3($sPathItem & $sFile, $sFilter, $iFlag, $bRecursiv, $sExclude, 0)
          WEnd
          FileClose($hSearch)
        EndIf
      EndIf

    Next ;$iPCount

  Else ;If Not $sExclude

    For $iPCount = 1 To $aPath[0]
      Local $sPathItem = StringStripWS($aPath[$iPCount], 3) ;Strip leading and trailing spaces
      Local $sDelim = "|" ;reset $sDelim

      If StringRight($sPathItem, 1) <> "\" Then $sPathItem = $sPathItem & "\" ;check for trailing "\"

      ;return full-path or filename only ($iFlag 0,1,2 = Filename, $iFlag 4,5,6 = full path)
      If $iFlag > 3 Then
        $sDelim &= $sPathItem
        $TFlag = $iFlag - 4
      Else
        $TFlag = $iFlag
      EndIf

      ;perform the search
      For $iFCount = 1 To $aFilter[0]
        Local $FilterItem = StringStripWS($aFilter[$iFCount], 3) ;Strip leading and trailing spaces
        If StringRegExp($FilterItem, "[\\/:<>|]") Then ContinueLoop ;Look for bad chars
        $hSearch = FileFindFirstFile($sPathItem & $FilterItem)
        If @error Then ContinueLoop
        Switch $TFlag
          Case 0 ;Files and Folders
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              $sFileList &= $sDelim & $sFile
            WEnd
          Case 1 ;Files Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              ;If StringInStr(FileGetAttrib($sPath & $sFile), "D") <> 0 Then ContinueLoop; bypass folder (ver 3.3.0.0)
              If @extended Then ContinueLoop ;bypass folder (for Autoit versions > 3.3.0.0)
              $sFileList &= $sDelim & $sFile
            WEnd
          Case 2 ;Folders Only
            While True
              $sFile = FileFindNextFile($hSearch)
              If @error Then ExitLoop
              ;If StringInStr(FileGetAttrib($sPath & $sFile), "D") = 0 Then ContinueLoop; bypass file (ver 3.3.0.0)
              If @extended = 0 Then ContinueLoop ;bypass file (for Autoit versions > 3.3.0.0)
              $sFileList &= $sDelim & $sFile
            WEnd
          Case Else
            Return SetError(3, 3, "")
        EndSwitch
        FileClose($hSearch)
      Next

      ;---------------

      ;optional do a recursive search
      If $bRecursiv Then
        $hSearch = FileFindFirstFile($sPathItem & "*.*")
        If Not @error Then
          While True
            $sFile = FileFindNextFile($hSearch)
            If @error Then ExitLoop
            ;If StringInStr(FileGetAttrib($sPath & $sFile), "D") = 0 Then ContinueLoop; bypass file (ver 3.3.0.0)
            If @extended = 0 Then ContinueLoop ;bypass file (for Autoit versions > 3.3.0.0)
            ;call recursive search
            $sFileList &= _FileListToArrayEx3($sPathItem & $sFile, $sFilter, $iFlag, $bRecursiv, $sExclude, 0)
          WEnd
          FileClose($hSearch)
        EndIf
      EndIf

    Next ;$iPCount

  EndIf ;If $sExclude

  ;---------------

  ;Set according return value
  Switch $iRetFormat
    Case 0
      Return $sFileList
    Case 1
      If $sFileList = "" Then
        Local $aRet[1] = [0]
        Return $aRet
      Else
        Return StringSplit(StringTrimLeft($sFileList, 1), "|", $iRetFormat)
      EndIf
    Case 2
      If $sFileList = "" Then
        Local $aRet[0]
        Return $aRet
      Else
        Return StringSplit(StringTrimLeft($sFileList, 1), "|", $iRetFormat)
      EndIf
    Case Else
      Return SetError(4, 4, "")
  EndSwitch

EndFunc   ;==>_FileListToArrayEx3oÝ÷ Ø©®ÞuW«²*'¡ñbââ²ÔèºÚË©÷ùõ«­¢+ØìU9
 
Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...