Directory recursion

Applications, Games, Tools, User libs and useful stuff coded in PureBasic
User avatar
RichAlgeni
Addict
Addict
Posts: 914
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Directory recursion

Post by RichAlgeni »

I had a couple of people ask me about recursion lately, and I threw this together for them. I know sometimes things get lost, so here it is.

Code: Select all

EnableExplicit

#systemSlash = "\"

Define directoryName.s
Define NewList fileListName.s()
Define startTime.i
Define completeTime.i

Global directoryCount.i
Global fileCount.i

OpenConsole()

CompilerIf #PB_Compiler_Thread = 1
    CompilerError "This program is NOT thread safe"
CompilerEndIf

PrintN("This process will count the number of subdirectories and files in a diretory")
PrintN("")
PrintN("It will add the complete file name for each file found to a list")
PrintN("")
Print("Enter the directory name you wish to start with: ")
directoryName = Input()

Procedure.i RecurseThruDirectories(*directoryName, List fileListName.s())

    Protected fileName.s
    Protected newDirectory.s
    Protected directoryName.s
    Protected directoryNumber.i

; make sure our directory name ends with a system slash

    directoryName = PeekS(*directoryName)
    If Right(directoryName, 1) <> #systemSlash
        directoryName = directoryName + #systemSlash
    EndIf
    PrintN(directoryName)
    directoryCount + 1

; loop thru the entries in a directory

    directoryNumber = ExamineDirectory(#PB_Any, directoryName, "*.*")  
    While NextDirectoryEntry(directoryNumber)
        If DirectoryEntryType(directoryNumber) = #PB_DirectoryEntry_File
            fileName = directoryName + DirectoryEntryName(directoryNumber)
            AddElement(fileListName())
            fileListName() = fileName
            fileCount + 1
        Else
            newDirectory = DirectoryEntryName(directoryNumber)
            Select newDirectory
            Case "."
            Case ".."
            Default
                newDirectory = directoryName + newDirectory
                RecurseThruDirectories(@newDirectory, fileListName())
            EndSelect
        EndIf
    Wend

    FinishDirectory(directoryNumber)

EndProcedure

startTime = ElapsedMilliseconds()

RecurseThruDirectories(@directoryName, fileListName())

completeTime = ElapsedMilliseconds()

PrintN("")
PrintN("There were "+ Str(directoryCount) + " directories found")
PrintN("")
PrintN("There were "+ Str(fileCount) + " files found")
PrintN("")
PrintN("There are "+ Str(ListSize(fileListName())) + " files in the list")
PrintN("")
PrintN("This process took " + Str(completeTime - startTime) + " milliseconds to complete")
PrintN("")
Print("Press <enter> to end: ")

Input()
CloseConsole()
End
User avatar
jacdelad
Addict
Addict
Posts: 1478
Joined: Wed Feb 03, 2021 12:46 pm
Location: Planet Riesa
Contact:

Re: Directory recursion

Post by jacdelad »

Why do I need to use the threadsafe-option if you're not using threads?
Also I think it's better to use a non-recursive variant. But we had this discussion here before.
PureBasic 6.04/XProfan X4a/Embarcadero RAD Studio 11/Perl 5.2/Python 3.10
Windows 11/Ryzen 5800X/32GB RAM/Radeon 7770 OC/3TB SSD/11TB HDD
Synology DS1821+/36GB RAM/130TB
Synology DS920+/20GB RAM/54TB
Synology DS916+ii/8GB RAM/12TB
User avatar
RichAlgeni
Addict
Addict
Posts: 914
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Directory recursion

Post by RichAlgeni »

Look at bit closer. It is not thread safe, and it does not use threads

My post was merely to help those whoo might want to learn about recursion, nothing more.
User avatar
jacdelad
Addict
Addict
Posts: 1478
Joined: Wed Feb 03, 2021 12:46 pm
Location: Planet Riesa
Contact:

Re: Directory recursion

Post by jacdelad »

I'm sorry if this sounds rude now, but:
- don't put threadsafe in when threadsafe is not needed
- the program crashes with something like "directory not initialized" (I'm using the german version) when searching through "C:\Windows\System32", because you don't check if ExamineDirectory(...) returns success or not
- I don't see the point in using a recursive algorithm for that
- using the address instead of the string itself seems counterproductive

Again, I'm open for a conversation.
Last edited by jacdelad on Mon Feb 20, 2023 11:42 pm, edited 1 time in total.
PureBasic 6.04/XProfan X4a/Embarcadero RAD Studio 11/Perl 5.2/Python 3.10
Windows 11/Ryzen 5800X/32GB RAM/Radeon 7770 OC/3TB SSD/11TB HDD
Synology DS1821+/36GB RAM/130TB
Synology DS920+/20GB RAM/54TB
Synology DS916+ii/8GB RAM/12TB
User avatar
jacdelad
Addict
Addict
Posts: 1478
Joined: Wed Feb 03, 2021 12:46 pm
Location: Planet Riesa
Contact:

Re: Directory recursion

Post by jacdelad »

Code: Select all

Procedure.i RecurseThruDirectories(*directoryName, List fileListName.s())
  Protected NewList TempList.s(),exa,temp$,dir$
  AddElement(TempList())
  TempList()=PeekS(*directoryName)
  If Right(TempList(), 1) <> #systemSlash
    TempList() = TempList() + #systemSlash
  EndIf
  PrintN(TempList())
  directoryCount + 1
  
  While ListSize(TempList())
    FirstElement(TempList())
    dir$=TempList()
    exa=ExamineDirectory(#PB_Any,dir$,"*.*")
    If exa
      While NextDirectoryEntry(exa)
        temp$=DirectoryEntryName(exa)
        If DirectoryEntryType(exa)=#PB_DirectoryEntry_Directory
          If ReplaceString(temp$,".","")<>""
            directoryCount+1
            AddElement(TempList())
            TempList()=dir$+temp$+#systemSlash
          EndIf
        Else
          AddElement(fileListName())
          fileListName()=dir$+temp$
        EndIf
      Wend
    EndIf
    FirstElement(TempList())
    DeleteElement(TempList())
  Wend
  
  fileCount=ListSize(fileListName())
  
EndProcedure
Replace your function with this for the same result. I did several tests on which one is faster, but I don't see a valid difference.
PureBasic 6.04/XProfan X4a/Embarcadero RAD Studio 11/Perl 5.2/Python 3.10
Windows 11/Ryzen 5800X/32GB RAM/Radeon 7770 OC/3TB SSD/11TB HDD
Synology DS1821+/36GB RAM/130TB
Synology DS920+/20GB RAM/54TB
Synology DS916+ii/8GB RAM/12TB
AZJIO
Addict
Addict
Posts: 1364
Joined: Sun May 14, 2017 1:48 am

Re: Directory recursion

Post by AZJIO »

I specifically made an example for the help file.
viewtopic.php?p=595785#p595785
It's just a condition - add folders or files or both.
Last edited by AZJIO on Tue Feb 21, 2023 12:45 am, edited 1 time in total.
User avatar
RichAlgeni
Addict
Addict
Posts: 914
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Directory recursion

Post by RichAlgeni »

I'm not sure why it is so difficult for you to actually read the code I posted. The CompilerIf statement will throw an error if you try to compile the code with the option 'Create threadsafe executable' checked, therefore it does not use threads. As far as having a conversation, I'm also not sure how, given your erroneous interpretation of this small routine, I would benefit.
User avatar
Kiffi
Addict
Addict
Posts: 1357
Joined: Tue Mar 02, 2004 1:20 pm
Location: Amphibios 9

Re: Directory recursion

Post by Kiffi »

RichAlgeni wrote: Mon Feb 20, 2023 10:43 pm

Code: Select all

#systemSlash = "\"
Small hint: PureBasic provides a constant which contains the correct path separator depending on the operating system:
PB-History wrote:2nd January 2019 : Version 5.70 LTS

[...]
- Added: #PS, #NPS, #PS$ and #NPS$ constants (Path seperator character depending of the OS)
[...]
Hygge
User avatar
jacdelad
Addict
Addict
Posts: 1478
Joined: Wed Feb 03, 2021 12:46 pm
Location: Planet Riesa
Contact:

Re: Directory recursion

Post by jacdelad »

RichAlgeni wrote: Tue Feb 21, 2023 12:43 am I'm not sure why it is so difficult for you to actually read the code I posted. The CompilerIf statement will throw an error if you try to compile the code with the option 'Create threadsafe executable' checked, therefore it does not use threads. As far as having a conversation, I'm also not sure how, given your erroneous interpretation of this small routine, I would benefit.
Yes, you're right. I misinterpreted that. But why do you disallow running it when threadsafe is enabled? This doesn't make sense, too.
PureBasic 6.04/XProfan X4a/Embarcadero RAD Studio 11/Perl 5.2/Python 3.10
Windows 11/Ryzen 5800X/32GB RAM/Radeon 7770 OC/3TB SSD/11TB HDD
Synology DS1821+/36GB RAM/130TB
Synology DS920+/20GB RAM/54TB
Synology DS916+ii/8GB RAM/12TB
User avatar
Demivec
Addict
Addict
Posts: 4091
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Directory recursion

Post by Demivec »

@RichAlgeni: Thanks for providing some example code for individuals pondering recursion. The forum could always use more code examples and tutorials.

I have a small remark about your code and the use of a string pointer. In your code for procedure RecurseThruDirectories() you pass a pointer *directoryName. I presume you pass a pointer instead of a string so that you can avoid an unnecessary copy of the directoryName string.

However, in the procedure the first thing that is done is an immediate read of the string using the pointer and you store another copy of it. When recursing back into the procedure you pass a pointer to this new copy of the string and everything starts all over again. IMHO, this makes using a pointer to the directoryName instead of the actual string pointless (pun intended).
User avatar
STARGÅTE
Addict
Addict
Posts: 2089
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Directory recursion

Post by STARGÅTE »

@jacdelad:
The code posted by RichAlgeni is not threadsafe because he is using global counter variables inside RecurseThruDirectories().
Therefore you cannot use RecurseThruDirectories() in threads without intermixing these counts.
Here you can replace Global by Threaded.
However, directoryCount and fileCount are even not reseted, so the code is only working as it is.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
User avatar
RichAlgeni
Addict
Addict
Posts: 914
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Directory recursion

Post by RichAlgeni »

Demivec wrote: Tue Feb 21, 2023 3:50 amI have a small remark about your code and the use of a string pointer. In your code for procedure RecurseThruDirectories() you pass a pointer *directoryName. I presume you pass a pointer instead of a string so that you can avoid an unnecessary copy of the directoryName string.
I have never liked passing strings, I always pass pointers. Then, I used PeekS() as ExamineDirectory() does not take a pointer.

@kiffi: Yes, I know about the system delimiters, but since this was an example of recursion, I deemed it not a big deal.

@jacdelad: Recursion by its nature is not threaded, as a procedure is calling a new instance of itself. The purpose of this example wasn't to extoll the virtue of non-threaded code, it was simply an example of recursion for those who might be interested.
AZJIO
Addict
Addict
Posts: 1364
Joined: Sun May 14, 2017 1:48 am

Re: Directory recursion

Post by AZJIO »

Use the s.String structure, then you will have both a string and a pointer without having to use PeekS.

When you call a new function, you leave the previous function open, so you can have for example 6 functions open waiting for the child to be executed. If you have a pointer to store the path, then you will probably mess up the output for the parent function, because they continue to calculate data for the current path and this path should not be changed by the called function.

"Applications" section implies a ready-made application, and possibly without a source code.
User avatar
RichAlgeni
Addict
Addict
Posts: 914
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Directory recursion

Post by RichAlgeni »

AZJIO wrote: Tue Feb 21, 2023 7:04 pm Use the s.String structure, then you will have both a string and a pointer without having to use PeekS.
It's a difference without a distinction, so why does it matter?
AZJIO wrote: Tue Feb 21, 2023 7:04 pmIf you have a pointer to store the path, then you will probably mess up the output for the parent function, because they continue to calculate data for the current path and this path should not be changed by the called function.
The path isn't changed by the calling function, so your point is irrelevant.
AZJIO wrote: Tue Feb 21, 2023 7:04 pm"Applications" section implies a ready-made application, and possibly without a source code.
You honestly had nothing better to do?
User avatar
jacdelad
Addict
Addict
Posts: 1478
Joined: Wed Feb 03, 2021 12:46 pm
Location: Planet Riesa
Contact:

Re: Directory recursion

Post by jacdelad »

@STARGATE: You're right, I didn't mention that.

But again, and without trying to be counterproductive, I think this way (including the global variables) is not a good way to do the task.
PureBasic 6.04/XProfan X4a/Embarcadero RAD Studio 11/Perl 5.2/Python 3.10
Windows 11/Ryzen 5800X/32GB RAM/Radeon 7770 OC/3TB SSD/11TB HDD
Synology DS1821+/36GB RAM/130TB
Synology DS920+/20GB RAM/54TB
Synology DS916+ii/8GB RAM/12TB
Post Reply