Page 1 of 1

CountFiles()

Posted: Sun Jul 03, 2016 11:41 pm
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.

Re: CountFiles (Windows-specific)

Posted: Mon Jul 04, 2016 3:58 am
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\.

Re: CountFiles (Windows-specific)

Posted: Mon Jul 04, 2016 4:09 pm
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!

Re: CountFiles (Windows-specific)

Posted: Mon Jul 04, 2016 4:35 pm
by Thunder93
Just as easy to make a cross-platform supported version. ....Removed some text

Re: CountFiles (Windows-specific)

Posted: Mon Jul 04, 2016 4:45 pm
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?

Re: CountFiles (Windows-specific)

Posted: Mon Jul 04, 2016 5:13 pm
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.

Re: CountFiles (Windows-specific)

Posted: Mon Jul 04, 2016 5:59 pm
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:

Re: CountFiles (Windows-specific)

Posted: Mon Jul 04, 2016 6:30 pm
by Thunder93
Was that from running via Debugger or executable execution? Always even faster via executable execution :wink:

Re: CountFiles (Windows-specific)

Posted: Mon Jul 04, 2016 7:59 pm
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) :)

Re: CountFiles (Windows-specific)

Posted: Tue Jul 05, 2016 5:13 am
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