CountFiles()

Share your advanced PureBasic knowledge/code with the community.
User avatar
Thunder93
Addict
Addict
Posts: 1788
Joined: Tue Mar 21, 2006 12:31 am
Location: Canada

CountFiles()

Post by Thunder93 »

Code: Select all

Global NewList cfNames.s()
Global NewList cfTypes.s()

;{ Windows Specific Procedure }
Procedure.i CountFiles(Path.s, Ignore_SubDIRs.b = 0, State.b = 1)
  Protected _Data.WIN32_FIND_DATA, Path$ = Path + "\*.*", IsTrue.b
  Static Files
  
  If State : Files = 0 : EndIf
  
  hF = FindFirstFile_(Path$, @_Data)
  
  If hF <> #INVALID_HANDLE_VALUE    
    Repeat     
      If _Data\dwFileAttributes & #FILE_ATTRIBUTE_DIRECTORY
        If Ignore_SubDIRs = 1 : Continue : EndIf
        
        ;// skip file or directory that has an associated reparse point,
        ;// or a file that is a symbolic link. Basically to avoid potential
        ;// infinitely recursive directory tree.
        If Not _Data\dwFileAttributes & #FILE_ATTRIBUTE_REPARSE_POINT
          
          ;// make sure we skip "." And ".."
          ;// some file names can start With a dot, so just testing For the 
          ;// first dot is Not suffient.
          
          If PeekS(@_Data\cFileName) <> "." And PeekS(@_Data\cFileName) <> ".."
            
            ;// We found a sub-directory, so get the files in it too
            Path$ = Path + "\" + PeekS(@_Data\cFileName)
            
            ;// recurrsion here!            
            CountFiles(Path$, 0, 0)  ; Traverse Directory
          EndIf
        EndIf
      Else
        
        ForEach cfTypes()
          If cfTypes() = GetExtensionPart(PeekS(@_Data\cFileName))
            IsTrue = 1
            Break
          EndIf
        Next
        
        If IsTrue = 1 : IsTrue = 0 : Continue : EndIf
        
        
        ForEach cfNames()
          If cfNames() = GetFilePart(PeekS(@_Data\cFileName))
            IsTrue = 1
            Break
          EndIf            
        Next
        
        If IsTrue = 1 : IsTrue = 0 : Continue : EndIf
        
        Files + 1        
      EndIf 
    Until FindNextFile_(hF, @_Data) = 0
    FindClose_(hF)    
  EndIf
  
  ProcedureReturn Files
EndProcedure
;}

Procedure.b AddFiletype(Str$)  
  If AddElement(cfTypes()) <> 0
    cfTypes() = Str$
    ProcedureReturn 1
  EndIf
EndProcedure

Procedure.b AddFilename(Str$)
  If AddElement(cfNames()) <> 0
    cfNames() = Str$
    ProcedureReturn 1
  EndIf
EndProcedure

Procedure.b DelFiletype(Str$)
  ForEach cfTypes()
    If cfTypes() = Str$
      DeleteElement(cfTypes())
      ProcedureReturn 1
    EndIf
  Next
EndProcedure

Procedure.b DelFilename(Str$)
  ForEach cfNames()
    If cfNames() = Str$
      DeleteElement(cfNames())
      ProcedureReturn 1
    EndIf
  Next
EndProcedure

Procedure.b ClearFilenames()
  ClearList(cfNames())
EndProcedure

Procedure.b ClearFiletypes()
  ClearList(cfTypes())
EndProcedure

CompilerIf #PB_Compiler_IsMainFile
  Path.s = #PB_Compiler_Home
  Debug "CountFiles()"
  Debug "Path: " + Path
  Debug "  "+CountFiles(Path, 1) + " Files. Excluded SubDirectories."
  ;   Debug "  "+CountFiles(Path) + " Regular, Include SubDirectories."
  
  AddFiletype("exe")
  Debug "  "+CountFiles(Path, 1) + " Files. Excluded executables filetypes. Excluded SubDirectories."
  
  DelFiletype("exe")
  AddFilename("PureBasic.exe")
  Debug "  "+CountFiles(Path, 1) + " Files. Excluded file: PureBasic.exe. Excluded SubDirectories."  
  
  AddFilename("unins000.exe")
  Debug "  "+CountFiles(Path, 1) + " Files. Excluded files: PureBasic.exe and unins000.exe. Excluded SubDirectories."  
  
  ClearFilenames() : ClearFiletypes()
  Debug " "
  Debug "  "+CountFiles(Path) + " Files. SubDirectories Included."
  
  AddFiletype("exe")
  Debug "  "+CountFiles(Path) + " Files. Excluded executables filetypes. SubDirectories Included."
  
  AddFilename("PureBasic.chm")
  Debug "  "+CountFiles(Path) + " Files. Excluded file: PureBasic.chm. Excluded executables filetypes. Included SubDirectories."  
CompilerEndIf

Debugging Output:

CountFiles()
Path: D:\Program Files\PureBasic 5.42\
  • 4 Files. Excluded SubDirectories.
    2 Files. Excluded executables filetypes. Excluded SubDirectories.
    3 Files. Excluded file: PureBasic.exe. Excluded SubDirectories.
    2 Files. Excluded files: PureBasic.exe and unins000.exe. Excluded SubDirectories.

    937 Files. SubDirectories Included.
    922 Files. Excluded executables filetypes. SubDirectories Included.
    921 Files. Excluded file: PureBasic.chm. Excluded executables filetypes. Included SubDirectories.

Click Here to jump to the later post with enhanced version of CountFiles(), also using PB native commandset.
Last edited by Thunder93 on Tue Jul 05, 2016 4:13 pm, edited 3 times in total.
ʽʽSuccess is almost totally dependent upon drive and persistence. The extra energy required to make another effort or try another approach is the secret of winning.ʾʾ --Dennis Waitley
User avatar
Thunder93
Addict
Addict
Posts: 1788
Joined: Tue Mar 21, 2006 12:31 am
Location: Canada

Re: CountFiles (Windows-specific)

Post by Thunder93 »

Little more enhanced.

Code: Select all

Global NewList cfNames.s()
Global NewList cfTypes.s()
Global NewList cDirectories.s()

;{ Windows Specific Procedure }
Procedure.i CountFiles(Path.s, Ignore_SubDIRs.b = 0, State.b = 1)
  Protected _Data.WIN32_FIND_DATA, Path$ = Path + "\*.*", IsTrue.b, StrAd.s = "\"
  Static Files
  
  If Right(Path, 1) = "\" : StrAd = "" : EndIf
  
  If State : Files = 0 : EndIf
  
  hF = FindFirstFile_(Path$, @_Data)
  
  If hF <> #INVALID_HANDLE_VALUE    
    Repeat     
      If _Data\dwFileAttributes & #FILE_ATTRIBUTE_DIRECTORY
       If Ignore_SubDIRs = 1 : Continue : EndIf
        
        ;// skip file or directory that has an associated reparse point,
        ;// or a file that is a symbolic link. Basically to avoid potential
        ;// infinitely recursive directory tree.
        If Not _Data\dwFileAttributes & #FILE_ATTRIBUTE_REPARSE_POINT
          
          ;// make sure we skip "." And ".."
          ;// some file names can start With a dot, so just testing For the 
          ;// first dot is Not suffient.
          
          If PeekS(@_Data\cFileName) <> "." And PeekS(@_Data\cFileName) <> ".."
            
            ;// We found a sub-directory, so get the files in it too
            Path$ = Path + StrAd + PeekS(@_Data\cFileName)
            
            ForEach cDirectories()
              If cDirectories() = LCase(fName)
                IsTrue = 1
                Break
              EndIf
            Next
            
            If IsTrue = 1 : IsTrue = 0 : Continue : EndIf
            
            ;// recurrsion here!            
            CountFiles(Path$, 0, 0)  ; Traverse Directory
          EndIf
        EndIf
      Else
        
        ForEach cfTypes()
          If cfTypes() = LCase(GetExtensionPart(PeekS(@_Data\cFileName)))
            IsTrue = 1
            Break
          EndIf
        Next
        
        If IsTrue = 1 : IsTrue = 0 : Continue : EndIf
        
        
        ForEach cfNames()
          If cfNames() = LCase(GetFilePart(PeekS(@_Data\cFileName)))
            IsTrue = 1
            Break
          EndIf            
        Next
        
        If IsTrue = 1 : IsTrue = 0 : Continue : EndIf
        
        Files + 1        
      EndIf 
    Until FindNextFile_(hF, @_Data) = 0
    FindClose_(hF)    
  EndIf
  
  ProcedureReturn Files
EndProcedure
;}

Procedure.b AddFilename(Str$)
  If AddElement(cfNames()) <> 0
    cfNames() = LCase(Str$)
    ProcedureReturn 1
  EndIf
EndProcedure

Procedure.b AddFiletype(Str$)  
  If AddElement(cfTypes()) <> 0
    cfTypes() = LCase(Str$)
    ProcedureReturn 1
  EndIf
EndProcedure

Procedure.b AddDirectory(Str$)
  If Right(Str$, 1) = "\" : Str$ = Mid(Str$, 0, Len(Str$)-1) : EndIf
  
  If AddElement(cDirectories()) <> 0
    cDirectories() = LCase(Str$)
    ProcedureReturn 1
  EndIf
EndProcedure

Procedure.b DelFilename(Str$)
  ForEach cfNames()
    If cfNames() = LCase(Str$)
      DeleteElement(cfNames())
      ProcedureReturn 1
    EndIf
  Next
EndProcedure

Procedure.b DelFiletype(Str$)
  ForEach cfTypes()
    If cfTypes() = LCase(Str$)
      DeleteElement(cfTypes())
      ProcedureReturn 1
    EndIf
  Next
EndProcedure

Procedure.b DelDirectory(Str$)
  If Right(Str$, 1) = "\" : Str$ = Mid(Str$, 0, Len(Str$)-1) : EndIf
  
  ForEach cDirectories()
    If cDirectories() = LCase(Str$)
      DeleteElement(cDirectories())
      ProcedureReturn 1
    EndIf
  Next
EndProcedure

Procedure.b ClearFilenames()
  ClearList(cfNames())
EndProcedure

Procedure.b ClearFiletypes()
  ClearList(cfTypes())
EndProcedure

Procedure.b ClearDirectories()
  ClearList(cDirectories())
EndProcedure

Procedure.b ClearFilters()
  ClearList(cfNames()) : ClearList(cfTypes()) : ClearList(cDirectories())
EndProcedure

CompilerIf #PB_Compiler_IsMainFile
  Path.s = #PB_Compiler_Home

  Debug "CountFiles()"
  Debug "Path: " + Path
  Debug "  "+CountFiles(Path, 1) + " Files. Excluded SubDirectories."
  ;   Debug "  "+CountFiles(Path) + " Regular, Include SubDirectories."
  
  AddFiletype("exe") ; Add filetype to be excluded from search.
  Debug "  "+CountFiles(Path, 1) + " Files. Excluded executables filetypes. Excluded SubDirectories."
  
  DelFiletype("exe") ; Add filetype to be excluded from search.
  AddFilename("PureBasic.exe") ; Add file to be excluded from search.
  Debug "  "+CountFiles(Path, 1) + " Files. Excluded file: PureBasic.exe. Excluded SubDirectories."  
  
  AddFilename("unins000.exe") ; Add another file to be excluded from search.
  Debug "  "+CountFiles(Path, 1) + " Files. Excluded files: PureBasic.exe and unins000.exe. Excluded SubDirectories."  
  
  ClearFilters() ; Clear all Filters
  Debug " "
  Debug "  "+CountFiles(Path) + " Files. SubDirectories Included."
  
  AddFiletype("exe") ; Add filetype to be excluded from search.
  Debug "  "+CountFiles(Path) + " Files. Excluded executables filetypes. SubDirectories Included."
  
  AddFilename("PureBasic.chm") ; Add file to be excluded from search.
  Debug "  "+CountFiles(Path) + " Files. Excluded file: PureBasic.chm. Excluded executables filetypes. Included SubDirectories."  
  
  ClearFilters() ; Clear all Filters
  AddDirectory("D:\Program Files\PureBasic 5.42\Catalogs\Francais") ; Add Directory to be excluded from search.
  Debug "  "+CountFiles(Path) + " Files. Excluded SubDirectory ...\Catalogs\Francais\. SubDirectories Included."
CompilerEndIf
CountFiles()
Path: D:\Program Files\PureBasic 5.42\
  • 4 Files. Excluded SubDirectories.
    2 Files. Excluded executables filetypes. Excluded SubDirectories.
    3 Files. Excluded file: PureBasic.exe. Excluded SubDirectories.
    2 Files. Excluded files: PureBasic.exe and unins000.exe. Excluded SubDirectories.

    937 Files. SubDirectories Included.
    922 Files. Excluded executables filetypes. SubDirectories Included.
    921 Files. Excluded file: PureBasic.chm. Excluded executables filetypes. Included SubDirectories.
    933 Files. Included SubDirectories. Excluded SubDirectory ...\Catalogs\Francais\.
Last edited by Thunder93 on Tue Jul 05, 2016 4:20 pm, edited 1 time in total.
ʽʽSuccess is almost totally dependent upon drive and persistence. The extra energy required to make another effort or try another approach is the secret of winning.ʾʾ --Dennis Waitley
User avatar
blueb
Addict
Addict
Posts: 1116
Joined: Sat Apr 26, 2003 2:15 pm
Location: Cuernavaca, Mexico

Re: CountFiles (Windows-specific)

Post by blueb »

Very fast! :D

CountFiles()
Path: E:\PureBasic\
43 Files. Excluded SubDirectories.
34 Files. Excluded executables filetypes. Excluded SubDirectories.
42 Files. Excluded file: PureBasic.exe. Excluded SubDirectories.
41 Files. Excluded files: PureBasic.exe and unins000.exe. Excluded SubDirectories.

50750 Files. SubDirectories Included.
49420 Files. Excluded executables filetypes. SubDirectories Included.
49419 Files. Excluded file: PureBasic.chm. Excluded executables filetypes. Included SubDirectories.
E:\PureBasic\Catalogs\Francais
50746 Files. Excluded SubDirectory ...\Catalogs\Francais\. SubDirectories Included.

(I checked and there are 4 files in the above sub-directory)

This took 914 milliseconds!
- It was too lonely at the top.

System : PB 6.21(x64) and Win 11 Pro (x64)
Hardware: AMD Ryzen 9 5900X w/64 gigs Ram, AMD RX 6950 XT Graphics w/16gigs Mem
User avatar
Thunder93
Addict
Addict
Posts: 1788
Joined: Tue Mar 21, 2006 12:31 am
Location: Canada

Re: CountFiles (Windows-specific)

Post by Thunder93 »

Just as easy to make a cross-platform supported version. ....Removed some text
Last edited by Thunder93 on Tue Jul 05, 2016 3:55 am, edited 1 time in total.
ʽʽSuccess is almost totally dependent upon drive and persistence. The extra energy required to make another effort or try another approach is the secret of winning.ʾʾ --Dennis Waitley
User avatar
Kukulkan
Addict
Addict
Posts: 1396
Joined: Mon Jun 06, 2005 2:35 pm
Location: germany
Contact:

Re: CountFiles (Windows-specific)

Post by Kukulkan »

Short question: Is PB not simply using FindFirstFile_() and FindNextFile_() API in the background if we use the PB native functions? Is this function really faster than PB only? And why?
User avatar
Thunder93
Addict
Addict
Posts: 1788
Joined: Tue Mar 21, 2006 12:31 am
Location: Canada

Re: CountFiles (Windows-specific)

Post by Thunder93 »

I never analyzed but I believe PB uses FindFirstFile_() and FindNextFile_() API. Using directly the APIs can shave off a little processing time. Typically not significant enough to be concerned about.

Blueb can come on here and correct me if I'm wrong, however I imagine what he meant by “very fast”. For what it does all, its speedy. Not a comparison to the PB native counterpart.
Last edited by Thunder93 on Mon Jul 04, 2016 6:06 pm, edited 1 time in total.
ʽʽSuccess is almost totally dependent upon drive and persistence. The extra energy required to make another effort or try another approach is the secret of winning.ʾʾ --Dennis Waitley
User avatar
blueb
Addict
Addict
Posts: 1116
Joined: Sat Apr 26, 2003 2:15 pm
Location: Cuernavaca, Mexico

Re: CountFiles (Windows-specific)

Post by blueb »

That's right Thunder93.

As you can see from my previous post, I have a lot of files in my PureBasic directory...
4.36 GB (4,685,593,928 bytes) to be exact,
and I can run the example program in under 1 second.
Using a mid-range CPU (i7-2600 CPU)

Fast enough for most uses. :mrgreen:
- It was too lonely at the top.

System : PB 6.21(x64) and Win 11 Pro (x64)
Hardware: AMD Ryzen 9 5900X w/64 gigs Ram, AMD RX 6950 XT Graphics w/16gigs Mem
User avatar
Thunder93
Addict
Addict
Posts: 1788
Joined: Tue Mar 21, 2006 12:31 am
Location: Canada

Re: CountFiles (Windows-specific)

Post by Thunder93 »

Was that from running via Debugger or executable execution? Always even faster via executable execution :wink:
ʽʽSuccess is almost totally dependent upon drive and persistence. The extra energy required to make another effort or try another approach is the secret of winning.ʾʾ --Dennis Waitley
User avatar
blueb
Addict
Addict
Posts: 1116
Joined: Sat Apr 26, 2003 2:15 pm
Location: Cuernavaca, Mexico

Re: CountFiles (Windows-specific)

Post by blueb »

Nope... used sample program and Debug.

Removed Debug commands to check for differences...

Now the speed is: 839 milliseconds for the EXE (small difference) :)
- It was too lonely at the top.

System : PB 6.21(x64) and Win 11 Pro (x64)
Hardware: AMD Ryzen 9 5900X w/64 gigs Ram, AMD RX 6950 XT Graphics w/16gigs Mem
User avatar
Thunder93
Addict
Addict
Posts: 1788
Joined: Tue Mar 21, 2006 12:31 am
Location: Canada

Re: CountFiles (Windows-specific)

Post by Thunder93 »

The enhanced version, however uses PB native commandset.

Code: Select all

Global NewList cfNames.s()
Global NewList cfTypes.s()
Global NewList cDirectories.s()

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  #PB_FileSystem_Link = #FILE_ATTRIBUTE_REPARSE_POINT
CompilerEndIf


Procedure CountFiles(Path.s, Ignore_SubDIRs.b = 0, State.b = 1)
  Protected Path$, IsTrue.b, StrAd.s = "\"
  Static Files
  
  If Right(Path, 1) = "\" : StrAd = "" : EndIf
  
  If State : Files = 0 : EndIf
  
  
  hF = ExamineDirectory(#PB_Any, Path, "*.*")
  If hF <> #False   
    While NextDirectoryEntry(hF)
      If DirectoryEntryType(hF) = #PB_DirectoryEntry_Directory        
        If Ignore_SubDIRs = 1: Continue : EndIf
        
        If DirectoryEntryName(hF) <> "." And DirectoryEntryName(hF) <> ".."

          If DirectoryEntryAttributes(hF) & #PB_FileSystem_Link
            Continue
          EndIf
          
          Path$ = Path + StrAd + DirectoryEntryName(hF)
          
          ForEach cDirectories()
            If cDirectories() = LCase(Path$)
              IsTrue = 1
              Break
            EndIf
          Next
          
          If IsTrue = 1 : IsTrue = 0 : Continue : EndIf
          
          ;// recurrsion here!            
          CountFiles(Path$, 0, 0) ; Traverse Directory
        EndIf
      Else
        
        ForEach cfTypes()
          If cfTypes() = LCase(GetExtensionPart(DirectoryEntryName(hF)))
            IsTrue = 1
            Break
          EndIf
        Next
        
        If IsTrue = 1 : IsTrue = 0 : Continue : EndIf
        
        
        ForEach cfNames()
          If cfNames() = LCase(GetFilePart(DirectoryEntryName(hF)))
            IsTrue = 1
            Break
          EndIf            
        Next
        
        If IsTrue = 1 : IsTrue = 0 : Continue : EndIf
        
        Files + 1        
      EndIf 
      
    Wend
    FinishDirectory(hF)
  EndIf
  
  ProcedureReturn Files
EndProcedure

Procedure.b AddFilename(Str$)
  If AddElement(cfNames()) <> 0
    cfNames() = LCase(Str$)
    ProcedureReturn 1
  EndIf
EndProcedure

Procedure.b AddFiletype(Str$)  
  If AddElement(cfTypes()) <> 0
    cfTypes() = LCase(Str$)
    ProcedureReturn 1
  EndIf
EndProcedure

Procedure.b AddDirectory(Str$)
  If Right(Str$, 1) = "\" : Str$ = Mid(Str$, 0, Len(Str$)-1) : EndIf
  
  If AddElement(cDirectories()) <> 0
    cDirectories() = LCase(Str$)
    ProcedureReturn 1
  EndIf
EndProcedure

Procedure.b DelFilename(Str$)
  ForEach cfNames()
    If cfNames() = LCase(Str$)
      DeleteElement(cfNames())
      ProcedureReturn 1
    EndIf
  Next
EndProcedure

Procedure.b DelFiletype(Str$)
  ForEach cfTypes()
    If cfTypes() = LCase(Str$)
      DeleteElement(cfTypes())
      ProcedureReturn 1
    EndIf
  Next
EndProcedure

Procedure.b DelDirectory(Str$)
  If Right(Str$, 1) = "\" : Str$ = Mid(Str$, 0, Len(Str$)-1) : EndIf
  
  ForEach cDirectories()
    If cDirectories() = LCase(Str$)
      DeleteElement(cDirectories())
      ProcedureReturn 1
    EndIf
  Next
EndProcedure

Procedure.b ClearFilenames()
  ClearList(cfNames())
EndProcedure

Procedure.b ClearFiletypes()
  ClearList(cfTypes())
EndProcedure

Procedure.b ClearDirectories()
  ClearList(cDirectories())
EndProcedure

Procedure.b ClearFilters()
  ClearList(cfNames()) : ClearList(cfTypes()) : ClearList(cDirectories())
EndProcedure

CompilerIf #PB_Compiler_IsMainFile
  Path.s = #PB_Compiler_Home
  
  Str$ = "CountFiles()"+#LF$
  Str$ + "Path: " + Path+#LF$+#LF$
  
  StartTime = ElapsedMilliseconds()
  
  Str$ + "  "+CountFiles(Path, 1) + " Files. Excluded SubDirectories."+#LF$
  ;   Debug "  "+CountFiles(Path) + " Regular, Include SubDirectories."
  
  AddFiletype("exe") ; Add filetype to be excluded from search.
  Str$ + "  " + CountFiles(Path, 1) + " Files. Excluded executables filetypes. Excluded SubDirectories."+#LF$
  
  DelFiletype("exe") ; Add filetype to be excluded from search.
  AddFilename("PureBasic.exe") ; Add file to be excluded from search.
  Str$ + "  " + CountFiles(Path, 1) + " Files. Excluded file: PureBasic.exe. Excluded SubDirectories."+#LF$
  
  AddFilename("unins000.exe") ; Add another file to be excluded from search.
  Str$ + "  " + CountFiles(Path, 1) + " Files. Excluded files: PureBasic.exe and unins000.exe. Excluded SubDirectories."+#LF$ 
  
  ClearFilters() ; Clear all Filters
  Str$ + " "+#LF$
  Str$ + "  " + CountFiles(Path) + " Files. SubDirectories Included."+#LF$
  
  AddFiletype("exe") ; Add filetype to be excluded from search.
  Str$ + "  " + CountFiles(Path) + " Files. Excluded executables filetypes. SubDirectories Included."+#LF$
  
  AddFilename("PureBasic.chm") ; Add file to be excluded from search.
  Str$ + "  " + CountFiles(Path) + " Files. Excluded file: PureBasic.chm. Excluded executables filetypes. Included SubDirectories."+#LF$ 
  
  ClearFilters() ; Clear all Filters
  AddDirectory("D:\Program Files\PureBasic 5.42\Catalogs\Francais") ; Add Directory to be excluded from search.
  Str$ + "  " + CountFiles(Path) + " Files. Included SubDirectories. Excluded SubDirectory ...\Catalogs\Francais\."+#LF$+#LF$
  
  ElapsedTime = ElapsedMilliseconds()-StartTime
  
  Str$ + "Runtime: " + Str(ElapsedTime)+"ms"
  
  SetClipboardText(Str$)
  
  MessageRequester("Information", Str$, #PB_MessageRequester_Ok)
  
CompilerEndIf
ʽʽSuccess is almost totally dependent upon drive and persistence. The extra energy required to make another effort or try another approach is the secret of winning.ʾʾ --Dennis Waitley
Post Reply