Sub-folders listing

604 views
Skip to first unread message

Pete

unread,
Oct 19, 2020, 12:13:26 PM10/19/20
to Harbour Users
Hi,

Inside a folder there are a few hundreds of subfolders and each one of them
contains some good thousands of files.
Question: what could be the best (i.e., optimal) way to get a list with the names
of those subfolders, sorted chronologically (by modification date)?
Note: we want list of **subfolders only**, not the files (there might be hundreds
of thousands or even millions files) inside those subfolders.
Note2: The easy way would be to use one of the available functions (Directory() etc),
Unfortunately none of them supports exclusive flags, (unless I'm badly wrong), which means
that these functions do process all the directory entries (files, directories, etc), something
which is not the most efficient approach in this case, I think.

Any hint/idea is welcome.

regards,
Pete

Auge & Ohr

unread,
Oct 19, 2020, 1:50:17 PM10/19/20
to Harbour Users
hi,

under MiniGUI you have the API Function GetFolder/( which open a Dialog.
it use BROWSEINFO() Structure to get Folder Name.

---

what about DIRECTORY("*.", "D")
Array might have some files without Extension but no "D" Attribut.
so you can build you own Folder Tree.

carlos

unread,
Oct 19, 2020, 2:04:02 PM10/19/20
to Harbour Users
Hi
not sure but..

dir /A:D /S /O:D >>dirs.txt

and parse the the file .txt

Carlos
Message has been deleted

Pete

unread,
Oct 28, 2020, 3:09:40 PM10/28/20
to Harbour Users
Ok, thanks for the hints!
(and sorry for the delayed return, very tense, very stressful days --and the covid strikes back, harshest this time! ;-(

@Auge
I do use minigui and am aware of `GetFolder()` but can't figure out how it could be helpful in this case. would you like to explain a little bit?
regarding DIRECTORY("*.", "D")  could be somehow useful  but what if there are directories with extensions? they will be left out.

@Carlos @ Rafal
I made a similar thought of using DIR command, but  prefer to not rely on OS services to do something which can be done by code.

It's interesting though that, despite the fact Harbour offers a good set of file handling functions,  we do not have a function able to filter
directory entries. Seems that never was in a high demand  such a functionality, perhaps.
Anyhow I used a modified version of DirScan() to work out the "case".

regards,
Pete




Auge & Ohr

unread,
Oct 28, 2020, 11:06:53 PM10/28/20
to Harbour Users
hi,

are you willing to install Everything ?
https://www.voidtools.com/

i have made a Sample here

it is much faster than travel through Subfolder

---

GetFolder() use BROWSEFORFOLDERTEXT Structure so that is the Part i mean.

Pete

unread,
Oct 29, 2020, 3:42:15 AM10/29/20
to Harbour Users
Hi,

On Thursday, 29 October 2020 05:06:53 UTC+2, Auge & Ohr wrote:
hi,

are you willing to install Everything ?
https://www.voidtools.com/


Well, maybe I could do this, in my own test-machine. But I don't like to do that on customers' PCs!
Besides,  I consider bad practice to rely on external tools to do what is doable programmatically
unless it is extremely hard or not feasible at all, in which case a third party tool is acceptable
(ideally an open source one), but this is only my personal opinion.

regards,
Pete

gianluigi pulix

unread,
Oct 29, 2020, 4:05:52 AM10/29/20
to Harbour Users

regards
Brunello Pulix


#include <common.ch>
#include <Directry.ch>
#include 'fileio.ch'


aOrigin   := {}              // Return Path+filename
aFolders  := {}              // Return Path+Foldername
cDir      := 'C:\MyFiles'
*
Scandir3(cDir,aOrigin,aFolders)
*

*
Static FUNCTION Scandir3(cDir,aRay,aPath)
  LOCAL i
  LOCAL a1
  LOCAL a2
  LOCAL nLen
  LOCAL c1
  *
  nLen := Len(cDir)+1
  *
  a1 := ReadDir3({cDir+'\'},aRay,nLen)
  FOR i := 1 TO Len(a1)
     c1 := SubStr(a1[i],nLen)
     c1 := Left(c1,Len(c1)-1)
     AAdd(aPath,c1)
  NEXT
  *
  DO WHILE .T.
     a2 := ReadDir3(a1,aRay,nLen)
     IF Empty(a2)
        EXIT
     ENDIF
     a1 := AClone(a2)
     FOR i := 1 TO Len(a1)
        c1 := SubStr(a1[i],nLen)
        c1 := Left(c1,Len(c1)-1)
        AAdd(aPath,c1)
     NEXT
  ENDDO
  *
RETURN
*
STATIC FUNCTION ReadDir3(a,aRay,nLen)
  LOCAL i
  LOCAL j
  LOCAL aDirTmp
  LOCAL aFiles
  *
  aDirTmp := {}
  FOR j := 1 TO Len(a)
     aFiles := Directory(a[j]+'','DH')
     *
     ASort(aFiles,,,{|x,y| x[1] < y[1] })
     *
     FOR i := 1 TO Len(aFiles)
        IF 'D' $ aFiles[i,F_ATTR].and. Left(aFiles[i,F_NAME],1) != '.'
           AAdd(aDirTmp,a[j]+aFiles[i,F_NAME]+'\')
        ELSEIF Left(aFiles[i,F_NAME],1) != '.'
           *
           AAdd(aRay,{;
           SubStr(a[j]+aFiles[i,F_NAME],nLen),;
           aFiles[i,F_SIZE],;
           Val(DToS(aFiles[i,F_DATE])),;
           Secs(aFiles[i,F_TIME]),;
           .F.,;
           Val(DToS(aFiles[i,F_DATE])+StrZero(Secs(aFiles[i,F_TIME]),5))})
           *
        ENDIF
     NEXT
  NEXT
  *
RETURN aDirTmp

Pete

unread,
Oct 29, 2020, 5:27:06 AM10/29/20
to Harbour Users
 
Hi Brunello!
I'll try out your function. I guess it'll be useful to other users too;
thanks for posting!

regards,
Pete

fdaniele

unread,
Oct 29, 2020, 9:09:20 AM10/29/20
to Harbour Users
Sig. Brunello ... sei un maestro !!!
 

Auge & Ohr

unread,
Oct 29, 2020, 12:59:04 PM10/29/20
to Harbour Users
hi,

are you willing to install Everything ?
https://www.voidtools.com/


Well, maybe I could do this, in my own test-machine. But I don't like to do that on customers' PCs!
 
OK ... you "just" ask for Folder.

Everything is a general Search Tool and use own Database so it is very fast.
i use it e.g. for Backup, search for Attribute "A", so i thought it will also work with "D" for you.

---

this Code from EDK do the same but Speed depend how much Subfolder you have

FUNCTION hb_DirScanM( cPath, aFileMask, cAttr, lrecursiv )
RETURN hb_DoScanM
( hb_DirSepAdd( hb_defaultValue( cPath, "" ) ), ;
                   IIF
( HB_ISARRAY( aFileMask ), aFileMask, IIF( HB_ISSTRING( aFileMask ), { aFileMask }, { hb_osFileMask() } ) ), ;
                   hb_defaultValue
( cAttr, "" ), ;
                   hb_ps
(), lrecursiv )


FUNCTION hb_doScanM( cPath, aMask, cAttr, cPathSep, lrecursiv )
LOCAL aFile
LOCAL lMatch
LOCAL aResult
:= {}

   DEFAULT lrecursiv TO
.T.

   FOR EACH aFile IN hb_vfDirectory
( cPath + hb_osFileMask(), cAttr + IF( lrecursiv, "D", "" ) )
      lMatch
:= .F.
      AEVAL
( aMask, { | x | IIF( HB_ISSTRING( x ), lMatch := hb_FileMatch( aFile[ F_NAME ], x ) .OR. lMatch, Nil ) } )
      IF
"D" $ aFile[ F_ATTR ]
         IF lMatch
.AND. "D" $ cAttr
            IF lrecursiv
= .T.
               AADD
( aResult, aFile )
            ENDIF
         ENDIF
         IF
!( aFile[ F_NAME ] == "." .OR. aFile[ F_NAME ] == ".." .OR. aFile[ F_NAME ] == "" )
            AEVAL
( hb_DoScanM( cPath + aFile[ F_NAME ] + cPathSep, aMask, cAttr, cPathSep ), ;
                   
{ | x | x[ F_NAME ] := aFile[ F_NAME ] + cPathSep + x[ F_NAME ], ;
                   AADD
( aResult, x ) } )
         ENDIF
      ELSEIF lMatch
         AADD
( aResult, aFile )
      ENDIF
   NEXT
RETURN aResult



mstuff kstuff

unread,
Oct 29, 2020, 11:15:14 PM10/29/20
to Harbour Users
Hi Pete,

Maybe check out:

hb_DirScan(“cPath”, “*.txt”, [<cAttr>]) aFiles 

found_array = hb_DirScan(…)
similar to hb_Directory() (refer to it for more..) with the difference that it scans not only <cPath> but also all subdirectories below <cPath> (recursively).

MikeK

Auge & Ohr

unread,
Oct 30, 2020, 4:14:55 PM10/30/20
to Harbour Users
Hi


hb_DirScan(“cPath”, “*.txt”, [<cAttr>]) aFiles 

found_array = hb_DirScan(…)
similar to hb_Directory() (refer to it for more..) with the difference that it scans not only <cPath> but also all subdirectories below <cPath> (recursively).



EDK Version can use Array of Extension e.g. all Type of Image

Appliserver

unread,
Nov 2, 2020, 3:26:03 AM11/2/20
to harbou...@googlegroups.com

Here is a recursive function that scans all the subdirectories. It can perform an action on every element found.

// dirscan - scans recursively directories and performs <cAction> on every element
function DirScan(cFileMask,cPath,cUfunc,xValue,nDepth,nMaxdepth)
  local aDirContent,count:=0,zz

  #IFDEF __XHARBOUR__
  local pFunc:=hb_funcPtr(cUfunc)
  #ELSE
  local pFunc:=__dynsN2Sym("cUfunc")
  #ENDIF

  if empty(pFunc)
    outStd (cUfunc+": not found")
    return nil
  endif
  DEFAULT nDepth to 1
  if right(cpath,1)!=hb_osPathSeparator()
    cpath+=hb_osPathSeparator()
  endif
  aDirContent=GetDirContent(cPath,cFileMask)
  if len (aDirContent)>0
    for zz:=1 to len (aDirContent)
      if (nDepth<nMaxdepth.or.nMaxdepth=0)
        if "D" $ aDirContent[zz,5]
            dirScan(cFileMask,cPath+aDirContent[zz,1],cUfunc,xValue,++nDepth,nMaxDepth)
            --nDepth
        endif
      endif
        #IFDEF __XHARBOUR__
           hb_exec(pFunc,nil,aDirContent[zz],xValue,cPath,cFileMask)
        #ELSE
            hb_execFromArray({pfunc,aDirContent[zz],xValue,cPath,cFileMask})
        #ENDIF
    next zz
  else
    if nDepth>0
        #IFDEF __XHARBOUR__
            hb_exec(pFunc,nil,{cPath,,,,"D"},xValue,"",cFileMask)
        #ELSE
            hb_execFromArray({pfunc,,{cPath,,,,"D"},xValue,"",cFileMask})
        #ENDIF
    endif
  endif
return nil

Try with cAction="calcsize"

function calcsize(aFileArr,dDate,cUpdir,cWc)
  if .not. "D" $ aFilearr[5]
    nTot+=aFileArr[2]
  endif
return nil

(You need a "static nTot" in the main prg)

HTH,

Dan

--
--
You received this message because you are subscribed to the Google
Groups "Harbour Users" group.
Unsubscribe: harbour-user...@googlegroups.com
Web: http://groups.google.com/group/harbour-users

---
You received this message because you are subscribed to the Google Groups "Harbour Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to harbour-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/harbour-users/c248bdc6-4d80-4f22-a2a6-25796254f527o%40googlegroups.com.

Appliserver

unread,
Nov 2, 2020, 8:31:50 AM11/2/20
to harbou...@googlegroups.com

And this is the missing getdircontent()... oops...

// content of a directory, sorted, dir first, then files, subarrays {filename, filesize...}
// getDirContent(Path-terminating-with separator, wildcard mask)-> aDir
func getDirContent(tpp,tpf)
  LOCAL nStartAt := 1
  TPL:=TPP+TPF
  aDir := DIRECTORY(TPL, "HSD")
  if TPF != "*.*" .and. .not. (tpf == "*")
    aXdir :=DIRECTORY(tpP+"*.*", "HSD")
    Aeval( aXdir,{|x,y|if("D" $ x.fileattrib,aadd(adir,x),nil)} )
  endif
  i := 1
  WHILE i <= LEN(aDir)
    IF ("D" $ aDir[i].fileattrib)
       IF (aDir[i].filename == ".")
          // NO . and ..
          ADEL(aDir, i)
          aDir := ASIZE(aDir, LEN(aDir) - 1)
          LOOP
       ELSEIF (aDir[i].filename == "..")
          ADEL(aDir, i)
          aDir := ASIZE(aDir, LEN(aDir) - 1)
          LOOP
       ENDIF
    ENDIF
    i++
  END
  // all sub-directories at the beginning of the list
  ASORT(aDir,,, {|x,y| "D" $ x.fileattrib})
  // other files
  AEVAL(aDir, {|x,i| nStartAt := ;
  IF("D" $ x.fileattrib, i, nStartAt)})
  // Sort sub-directories only
  ASORT(aDir,1, nStartAt, {|x,y| x.filename < y.filename})
  nStartAt++
  ASORT(aDir, nStartAt,, {|x,y| x.filename < y.filename})
return aDir

Dan

Reply all
Reply to author
Forward
0 new messages