Anzahl Rekursionen limitieren?

Anfängerfragen zum Programmieren mit PureBasic.
oO0XX0Oo
Beiträge: 55
Registriert: 21.07.2017 22:36

Anzahl Rekursionen limitieren?

Beitrag von oO0XX0Oo »

Hi,

Das Original-Modul stammt von hier: http://www.purebasic.fr/english/viewtop ... 12&t=45905

Ich bin dabei, es für meine Zwecke etwas anzupassen / ändern (bin auch soweit durch, aber eine Sache fehlt)...

Und zwar hätte ich gern die Option der Limitierung der Ordnertiefe, aber ich scheitere an der Logik dafür :oops:

Aus der Beschreibung:
; subLevels = -1 -> Full recursion (no limitation) [default]
; subLevels = 0 -> No recursion (stay in root folder), switches recursive to #False
; subLevels >= 1 -> Descend the hierarchy <x> subLevels deep
Das hier ist der Funktionsaufruf zum Testen:

Code: Alles auswählen

  cntItems = ScanDir::ScanDirRecursive(ScanDirInfo(), "D:\Tools", "*.bat", #True, 1)
  If cntItems >= 1
    ForEach ScanDirInfo()
      If ScanDirInfo()\Type = 1
        Debug "File: " + ScanDirInfo()\Item
      EndIf
    Next
  EndIf
In diesem Fall also eine Limitierung auf eine Ebene tiefer unterhalb von D:\Tools

Korrekt gefunden wird z.B.:

Code: Alles auswählen

D:\Tools\7-Zip\@Delete Languages.bat 
Gefunden wird aber auch:

Code: Alles auswählen

D:\Tools\@Command Line Tools\ADB\Reboot to download mode.bat
Nur ist diese .bat Datei zwei Ebenen tiefer, nicht eine...

Mag jemand mal draufschauen, wie man das korrekt implementiert?

Hier das Modul:

Code: Alles auswählen


#PB_Compiler_IsIncludeFile = #True


; *************************************************************************************************

; Original module: http://www.purebasic.fr/english/viewtopic.php?f=12&t=45905

; Scans one / multiple directories (multiple ones must be separated by "|" or #CRLF$) for
; files / folders
; Lower- / uppercase does NOT matter, neither for directory, nor extensions or excludes
; subLevels = -1 -> Full recursion (no limitation) [default]
; subLevels =  0 -> No recursion (stay in root folder), switches recursive to #False
; subLevels >= 1 -> Descend the hierarchy <x> subLevels deep
; extensions must be separated by ";" and given WITHOUT the typical leading "*." part
; excludes are treated in the same way like extensions

DeclareModule ScanDir

  Structure _ScanDirInfo
    Item.s                        ; Full file / folder name
    Type.b                        ; 1 = file, 2 = folder
    ;Size.q                        ; Size of file (not used for folders)
    ;DateC.i                       ; Creation date
    ;DateM.i                       ; Last modified date
    ;DateA.i                       ; Last accessed date
    ;Attributes.i                  ; Attributes
  EndStructure

  Global NewList ScanDirInfo._ScanDirInfo()

  EnumerationBinary
    #ScanDir_Folders              ; Includes directories in scan results (trailing separator included)
    #ScanDir_FoldersOnly          ; Excludes files from scan results (forces IncludeFolders flag)
  EndEnumeration


  Declare.i Clear()
  ; Supported flags: #ScanDir_Folders & #ScanDir_FoldersOnly
  Declare.i ScanDirRecursive(List ScanDirInfo._ScanDirInfo(), directory.s, extensions.s="", recursive.b=#True, subLevels=-1, flags=#Null, excludes.s="")

EndDeclareModule


; *************************************************************************************************
; *************************************************************************************************


Module ScanDir

  EnableExplicit

  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    #OS_Sep = "\"
  CompilerElse
    #OS_Sep = "/"
  CompilerEndIf


  Declare.b IsStringInGroup(string.s, group.s, sep.s=";")
  Declare.i Clear()
  Declare.i ScanDir(List ScanDirInfo._ScanDirInfo(), directory.s, extensions.s="", recursive.b=#True, subLevels=-1, flags=#Null, excludes.s="")
  ; Wrapper function for ScanDir()
  Declare.i ScanDirRecursive(List ScanDirInfo._ScanDirInfo(), directories.s, extensions.s="", recursive.b=#True, subLevels=-1, flags=#Null, excludes.s="")


; *************************************************************************************************

  ; Find a string (normally an extension) in a group of other strings
  Procedure.b IsStringInGroup(string.s, group.s, sep.s=";")
    Protected.b result = #False

    ; No string or group -> exit
    If string = "" Or group = ""
      ProcedureReturn result
    EndIf

    ; string and group are extended with leading + trailing seperator
    If FindString(sep + group + sep, sep + string + sep, 1, #PB_String_NoCase)
      result = #True
    EndIf
    ProcedureReturn result
  EndProcedure


; *************************************************************************************************

  ; Clear the list
  Procedure.i Clear()
    ClearList(ScanDirInfo())

    ProcedureReturn #True
  EndProcedure


; *************************************************************************************************

  ; Returns a list of files / folders
  ; If folders are included they will ignore all given extensions!
  ; Extensions must be separated by ";"
  ; Supported flags: #ScanDir_Folders & #ScanDir_FoldersOnly
  Procedure.i ScanDir(List ScanDirInfo._ScanDirInfo(), directory.s, extensions.s="", recursive.b=#True, subLevels=-1, flags=#Null, excludes.s="")
    Protected.i hDir
    Protected.b flagsFolders, flagsFoldersOnly
    Protected.s entryName, curExt

    Static.i cntLevel

    ; Eval flags
    flagsFolders     = Bool(flags & #ScanDir_Folders)
    flagsFoldersOnly = Bool(flags & #ScanDir_FoldersOnly)

    ; If we are searching for folders only, flagsFolders must be set to true by default
    If flagsFoldersOnly
      flagsFolders = #True
    EndIf

    hDir = ExamineDirectory(#PB_Any, directory, "*.*")
    If hDir
      While NextDirectoryEntry(hDir)
        entryName = DirectoryEntryName(hDir)

        ; Folders
        If DirectoryEntryType(hDir) = #PB_DirectoryEntry_Directory
          If entryName = "." Or entryName = ".."
            Continue
          EndIf

          ; Include folders flag set?
          If flagsFolders
            If AddElement(ScanDirInfo())
              With ScanDirInfo()
                \Item = directory + entryName + #OS_Sep
                \Type = 2 ; Folder
                ; Size is always zero for folders, exclude it
                ;\DateC = DirectoryEntryDate(hDir, #PB_Date_Created)
                ;\DateM = DirectoryEntryDate(hDir, #PB_Date_Modified)
                ;\DateA = DirectoryEntryDate(hDir, #PB_Date_Accessed)
                ;\Attributes = DirectoryEntryAttributes(hDir)
              EndWith
            EndIf
          EndIf

          ; Recurse into folder
          If recursive
            If subLevels <= -1
              ScanDir(ScanDirInfo(), directory + entryName + #OS_Sep, extensions, recursive, subLevels, flags, excludes)
            ElseIf subLevels > cntLevel
              cntLevel + 1
              ScanDir(ScanDirInfo(), directory + entryName + #OS_Sep, extensions, recursive, subLevels, flags, excludes)
            ElseIf cntLevel = subLevels
              cntLevel = 0
            EndIf
          EndIf

        ; Files
        Else
          ; Jump over any files?
          If flagsFoldersOnly
            Continue
          EndIf

          curExt = GetExtensionPart(entryName)

          If IsStringInGroup(curExt, excludes)
            Continue
          ElseIf IsStringInGroup(curExt, extensions) Or extensions = ""
            If AddElement(ScanDirInfo())
              With ScanDirInfo()
                \Item = directory + entryName
                \Type = 1 ; File
                ;\Size = DirectoryEntrySize(hDir)
                ;\DateC = DirectoryEntryDate(hDir, #PB_Date_Created)
                ;\DateM = DirectoryEntryDate(hDir, #PB_Date_Modified)
                ;\DateA = DirectoryEntryDate(hDir, #PB_Date_Accessed)
                ;\Attributes = DirectoryEntryAttributes(hDir)
              EndWith
            EndIf
          EndIf
        EndIf
      Wend
      FinishDirectory(hDir)
    EndIf

    ProcedureReturn ListSize(ScanDirInfo())
  EndProcedure


; *************************************************************************************************

  ; This is the wrapper to call ScanDir() multiple times if we pass more than one directory
  Procedure.i ScanDirRecursive(List ScanDirInfo._ScanDirInfo(), directories.s, extensions.s="", recursive.b=#True, subLevels=-1, flags=#Null, excludes.s="")
    Protected.i loops, i
    Protected.s directory

    ; If directories are separated by #CRLF$, replace it with "|"
    If FindString(directories, #CRLF$)
      directories = ReplaceString(directories, #CRLF$, "|")
    EndIf

    ; Remove "*." in extensions / excludes
    extensions = ReplaceString(extensions, "*.", "")
    excludes   = ReplaceString(excludes, "*.", "")

    ; Disable recursive when subLevels = 0 (stay only in the root folder)
    If subLevels = 0
      recursive = #False
    EndIf

    ; Loop over all passed directories
    loops = CountString(directories, "|") + 1
    For i = 1 To loops
      directory = StringField(directories, i, "|")

      ; Jump over all non-existing directories
      If Not FileSize(directory) = -2 : Continue : EndIf

      ; Make sure we have a trailing path separator
      If Right(directory, 1) <> #OS_Sep
        directory = directory + #OS_Sep
      EndIf

      ; Now call our real function
      ScanDir(ScanDirInfo._ScanDirInfo(), directory, extensions, recursive, subLevels, flags, excludes)
    Next

    ProcedureReturn ListSize(ScanDirInfo())
  EndProcedure

EndModule

Derren
Beiträge: 557
Registriert: 23.07.2011 02:08

Re: Anzahl Rekursionen limitieren?

Beitrag von Derren »

ich hab jetzt einfach mal einen Beispielcode gemacht:

Ich habe einen Ordnerstruktur C:\rekursion test\1\2\3\4\5\6 und C:\rekursion test\a\b\c\d\e\ usw
In einer statischen Variable zählst du quasi, wie oft schon Rekursion aufgetreten ist. Am Ende wird die Variable wieder zurückgesetzt. So gehst du immer maximal 4 (s. Variable "lim") Ordner-Ebenen unter deinen Root-Ordner.
Du kannst die Variable lim auch nicht global machen und immer als Parameter übergeben.
[/code]

Code: Alles auswählen

Global lim
lim = 4
Procedure ListPathContentRekursively(path.s)
	Protected dirID
	dirID = ExamineDirectory(#PB_Any, path, "*.*")
	
	Static a
	a+1
	Debug ">> "+Str(a)
	Debug "Content of> "+path
	
	
	While NextDirectoryEntry(dirID) 
		If DirectoryEntryName(dirID)<>"." And DirectoryEntryName(dirID)<>".."
			Debug DirectoryEntryName(dirID)
			If DirectoryEntryType(dirID) = #PB_DirectoryEntry_Directory
				If a<lim
					ListPathContentRekursively(path +"\" + DirectoryEntryName(dirID))
				EndIf 
				a=0
			EndIf
		EndIf 
	Wend 
EndProcedure 

ListPathContentRekursively("C:\rekursion test")
Signatur und so
oO0XX0Oo
Beiträge: 55
Registriert: 21.07.2017 22:36

Re: Anzahl Rekursionen limitieren?

Beitrag von oO0XX0Oo »

Danks sehr, Derren!

Allerdings hab ich zwei Probleme damit...

Tu mir bitte einen Gefallen und leg mal die folgende Ordnerstruktur an:

Code: Alles auswählen

C:\rekursion test
C:\rekursion test\gettags
C:\rekursion test\gettags\sub 1
C:\rekursion test\gettags\sub 2
C:\rekursion test\Git
C:\rekursion test\Git\Example
C:\rekursion test\Git\SendTo Profile
C:\rekursion test\Git\SendTo Profile\username
C:\rekursion test\Git\SendTo Profile\username\oO0XX0Oo
C:\rekursion test\Git\Userdata
Das hier ist nun der Output deines Beispiels mit lim = 2

Code: Alles auswählen

>> 1
Content of> C:\rekursion test
gettags
>> 2
Content of> C:\rekursion test\gettags
sub 1
sub 2
>> 1
Content of> C:\rekursion test\gettags\sub 2
Git
>> 1
Content of> C:\rekursion test\Git
Example
>> 2
Content of> C:\rekursion test\Git\Example
SendTo Profile
>> 1
Content of> C:\rekursion test\Git\SendTo Profile
username
>> 2
Content of> C:\rekursion test\Git\SendTo Profile\username
Userdata
>> 1
Content of> C:\rekursion test\Git\Userdata
2 Dinge passieren hier, die nicht vorgesehen sind:

01.

Code: Alles auswählen

Content of> C:\rekursion test\gettags\sub 1
fehlt... Der Ordner wird nicht beachtet
Das ist grundsätzlich immer der allererste Ordner, wenn man in der Verzeichnistiefe von lim
angekommen ist, der "aufgefressen" wird. Später auftretende Treffer in anderen Verzeichnissen
mit der gleiche Tiefe von lim werden dagegen beachtet.

02.

Code: Alles auswählen

Content of> C:\rekursion test\Git\SendTo Profile\username
oO0XX0Oo
Userdata
Dieser Ordner (username) sollte nicht mehr verarbeitet werden, er entspricht lim = 3

Danke und Gruß,
oO0XX0Oo
Antworten