Page 1 of 2
Directory recursion
Posted: Mon Feb 20, 2023 10:43 pm
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
Re: Directory recursion
Posted: Mon Feb 20, 2023 11:16 pm
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.
Re: Directory recursion
Posted: Mon Feb 20, 2023 11:26 pm
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.
Re: Directory recursion
Posted: Mon Feb 20, 2023 11:35 pm
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.
Re: Directory recursion
Posted: Mon Feb 20, 2023 11:41 pm
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.
Re: Directory recursion
Posted: Tue Feb 21, 2023 12:41 am
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.
Re: Directory recursion
Posted: Tue Feb 21, 2023 12:43 am
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.
Re: Directory recursion
Posted: Tue Feb 21, 2023 1:25 am
by Kiffi
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)
[...]
Re: Directory recursion
Posted: Tue Feb 21, 2023 2:35 am
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.
Re: Directory recursion
Posted: Tue Feb 21, 2023 3:50 am
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).
Re: Directory recursion
Posted: Tue Feb 21, 2023 8:55 am
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.
Re: Directory recursion
Posted: Tue Feb 21, 2023 5:21 pm
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.
Re: Directory recursion
Posted: Tue Feb 21, 2023 7:04 pm
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.
Re: Directory recursion
Posted: Tue Feb 21, 2023 8:15 pm
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?
Re: Directory recursion
Posted: Tue Feb 21, 2023 9:41 pm
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.