learning PureBasic

Everything else that doesn't fall into one of the other PB categories.
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

Re: learning PureBasic

Post by c4s »

@bosker
I think the recursion doesn't work properly. But because of those strange, short variable names (c.l, d.l, e.s, f.s, g.s) I can't see what's actually going on/wrong...

Remember "Good Coding Practice, Rule 1": Always create meaningful variable names! Also a couple of comments and empty lines to structure the code could help as well.
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: learning PureBasic

Post by Josh »

c4s wrote:I think the recursion doesn't work properly. But because of those strange, short variable names (c.l, d.l, e.s, f.s, g.s) I can't see what's actually going on/wrong...
The recursion can't work, because there is no recursion. Todays morning i had a look to this code, but after some minutes i aborted. This code with

- variable names c, d, e, g, s, g
- senseless variables like folders.s, crlf.s
- senseless parameters like pick.s
- and not at least a wrong result

is not readable for me. The code and using console reminds me programing in the early 1980, where i had a sheet with the description of each variable on the table.

It seems to me, some members here in the forum are meaning, each visitor of a driving school, has to start with cars manufactured latest 1920, because all newer cars have to much technique for newbies.
sorry for my bad english
bosker
Enthusiast
Enthusiast
Posts: 105
Joined: Fri Jan 08, 2010 11:04 pm
Location: Hampshire, UK

Re: learning PureBasic

Post by bosker »

@c4s/Josh - thanks for cheering me up. :)

I agree with your comments about single character names. I remember the days when that's all we had (and I'm glad they've gone).
I've done a bit of hacking about with the code so it's useful for me (and also it works). My recursive version below..

Code: Select all

EnableExplicit
Global nFolders.l, nFiles.l, nUsed.q    ; I hate globals (but I left them here)

; Scan a directory given in 'start'
; Uses recursion to scan subdirectories
; Returns an indication of whether or not the 'start' directory was valid
Procedure.i DirScan(start.s)
    Protected.i DirId
    Protected.s EntName

    DirId = ExamineDirectory(#PB_ANY, start, "")

    If DirId
        While NextDirectoryEntry(DirId)
            EntName = DirectoryEntryname(DirId)
            nUsed + DirectoryEntrySize(DirId)

            If DirectoryEntryType(DirId) = #PB_DirectoryEntry_File
                nFiles + 1
            ElseIf Left(EntName, 1) <> "."
                nFolders + 1
                DirScan(start + EntName + "\")
            EndIf
        Wend
    EndIf

    ProcedureReturn DirId
EndProcedure

; Get a number string with comma separators (eg 1,000,000)
Procedure.s CommaSep(number.q)
    Protected snum.s = Str(number), i.l

    For i = Len(snum) - 3 To 1 Step -3
        snum = InsertString(snum, ",", i + 1)
    Next

    ProcedureReturn snum
EndProcedure

; Follow the directory tree for each drive A..Z
; Returns string with list of drives actually visited ("C: D: E: " etc)
; NOTE: Some removable drives might cause problems here. eg if drive exists but no media present
Procedure.s WalkDrives()
    Protected AscDrive.l, Drive.s, Drives.s

    For AscDrive = Asc("C") To Asc("Z")
        Drive = Chr(AscDrive)
        nFolders = 0
        nFiles = 0
        nUsed = 0

        If DirScan(Drive + ":\")
            Print(Drive + ": has " + Str(nFolders) + " folders\subfolders and " + Str(nFiles))
            PrintN(" files on it ==> " + CommaSep(nused) + " bytes.")
            Drives + Drive + ": "
        EndIf
    Next

    ProcedureReturn Drives
EndProcedure

;================ M A I N   P R O G R A M =================
OpenConsole()
ConsoleTitle("PureBasic - Checking Drives, Folders, Files, and Bytes used.")

PrintN("")
PrintN("Drives present: " + WalkDrives())
PrintN("Press Enter to terminate.")
Input()
This might not work with some removable drives (eg printers that have SD card slots).
It gives the right sort of numbers on my C drive.
oldefoxx
Enthusiast
Enthusiast
Posts: 532
Joined: Fri Jul 25, 2003 11:24 pm

Re: learning PureBasic

Post by oldefoxx »

I'll take another look at the program. It follows the logic I've seen elsewhere, in that
you begin with ExamineDirectory() (has to preface everything), then uses NextDirectoryEntry()
to determine if there is another entry or not, then it uses DirectoryEntryName(), which would
either be a subfolder name or a file name, it adds in the DirectroyEntrySize(), it finally uses
DirectoryEntryType() to add the subfolder name or file name to the appropriate string, and finally
it closes with FinishDirectory() to release the resources required. It continues with the next
folder taken from the folder string, until the folder string is emply.

From what I've encountered, that seems to be what you have to do. I'm just carrying it to
another level by totaling up the folders, files, and sizes on a drive by drive basis.

If it is not coming up with the right numbers, then I don't know what might be missing. You
don't include in the Parent (".") and current ("..") directory indicators as part of the search of
folders. Could it be that I'm not getting the system, hidden, and read only files in that group?
Doesn't seem like there would be enough of them to make that much difference. Have to look
to see how you make sure they get included anyway.

If there is anyone that can see the problem here, I would sure like to know what it is that needs
to be covered as well. The Help file and example files don't give me any information that would
indicate something else is needed. All I can see to do is limit myself to a specific tree and its
branches (okay, path), and see if I can catch anything slipping by. That might only confirm that
there is a problem, not tell me what the problem is. Any advice out there?
has-been wanna-be (You may not agree with what I say, but it will make you think).
BorisTheOld
Enthusiast
Enthusiast
Posts: 542
Joined: Tue Apr 24, 2012 5:08 pm
Location: Ontario, Canada

Re: learning PureBasic

Post by BorisTheOld »

bosker wrote:@c4s/Josh - thanks for cheering me up. :)

I agree with your comments about single character names. I remember the days when that's all we had.......
Single character variable names? That was pure luxury.

In my day, programming was done using Unit Record patch panels. Plus we had to make our own paper tape loops and program cards for controlling printers and card punch machines.

Then at the end of the day we were hit on the head with a brick, and sent home bare foot through the snow. :mrgreen:
For ten years Caesar ruled with an iron hand, then with a wooden foot, and finally with a piece of string.
~ Spike Milligan
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: learning PureBasic

Post by Josh »

oldefoxx wrote:If there is anyone that can see the problem here, I would sure like to know what it is that needs to be covered as well. The Help file and example files don't give me any information that would indicate something else is needed.
The problem is, you are not using recursion. Recursion is the best and simpliest way to run through all folders and subfolders. Forgot all other solutions. The basic idea of recursion is, that you write a procedure, which is called by itself. The help file will give you no information about recursion, because recursion is nothing pb specific. You can find many examples here in the forum and essential knowledge about recursion find in the net.
signature of any board member wrote:To understand recursion you have to understand recursion
If you understand recursion, you will understand this slogan. :lol:
BorisTheOld wrote:Single character variable names? That was pure luxury.
Yes, i grow up in the golden age, where we could use real single character variables :mrgreen:
sorry for my bad english
bosker
Enthusiast
Enthusiast
Posts: 105
Joined: Fri Jan 08, 2010 11:04 pm
Location: Hampshire, UK

Re: learning PureBasic

Post by bosker »

@BorisTheOld
Then at the end of the day we were hit on the head with a brick, and sent home bare foot through the snow.
Luxury! We had to crawl home, licking the snow off the road with our tongues. Then we had to eat the brick for our tea.

Yes, I've used patch panels, paper tape and cards for programming too. Whole system builds done using only paper tape on board a submarine.
What joy. (I'm older than I look - but then you are a long way away)

@oldefoxx - I'll have another look at your original code. The string stuff inside FindFiles for building up subdirectories is much more complicated than using recursion and that's where I expect the problem is.
I've also filtered out the "." and ".." subdirectories and my version gives...
C: has 35071 folders\subfolders and 286404 files on it ==> 148,051,395,511 bytes.
This more or less agrees with other tools I have. More tomorrow.
bosker
Enthusiast
Enthusiast
Posts: 105
Joined: Fri Jan 08, 2010 11:04 pm
Location: Hampshire, UK

Re: learning PureBasic

Post by bosker »

I got the original code working but I had to make a lot of changes to help understand it.
I changed the variable names inside FindFiles...
c => FileCount, d => CRPos, e => CurrDir, f => EntName, g => CurrDir and added DirId.
I also got rid of the graphical console stuff - it messes with my console colours and I don't like that.
Variables declared but not required...
folders.s is never used. Removed.
files.s has something added to it but is otherwise never used. Removed.
crlf.s is unnecessary. PB has a predefined constant #CRLF$. crlf replaced with constant.
The pick parameter to FindFiles is only used to pass "". It's unnecessary. Removed.

The main problems with the logic (marked on the listing below by NOTE: ) were...
1. There's a '\' needed at the end of each directory name returned by DirectoryEntryName
2. At the final output print Str(b) should be Str(nfiles)

The main problem though is with the performance. Doing everything with strings can't compete with directory recursion.
To scan my C drive, this took 101.01 sec. My recursive version took 6.053 sec.

Code: Select all

EnableExplicit
Global nfolders.l, nfiles.l, nused.q

Procedure.l FindFiles(drivenpath.s)
  Protected.l FileCount, CRPos, DirId
  Protected.s CurrDir, EntName, StartPath

  StartPath = drivenpath

  If Right(StartPath, 1)<> "\"
    StartPath + "\"
  EndIf

  StartPath + #CRLF$

  Repeat
    CRPos = FindString(StartPath, #CRLF$)

    If CRPos = 0
        Break
    EndIf

    CurrDir = Left(StartPath, CRPos - 1)
    StartPath = Mid(StartPath, CRPos + 2)
    DirId = ExamineDirectory(#PB_Any, CurrDir, "")

    If DirId
      While NextDirectoryEntry(DirId)
        EntName = DirectoryEntryName(DirId)
        nused + DirectoryEntrySize(DirId)

        If DirectoryEntryType(DirId) = #PB_DirectoryEntry_Directory
          If EntName <> ".." And EntName <> "."
; NOTE: there's a '\' added on the next line
            StartPath + CurrDir + EntName + "\" + #CRLF$    
            nfolders + 1
          EndIf
        Else
          ;files + CurrDir + EntName + #CRLF$       ; this isn't used
          FileCount + 1
        EndIf

      Wend
      FinishDirectory(DirId)
    EndIf
  ForEver
  nfiles + FileCount
  ProcedureReturn FileCount
EndProcedure

Procedure.s cmm(number.q)
  Protected sum.s, a.l = 3
  sum = Str(number)

  While a<Len(sum)
    sum=Left(sum,Len(sum)-a)+","+Right(sum,a)
    a+4
  Wend

  ProcedureReturn sum
EndProcedure



Procedure WalkDrives()
  Protected a.l, b.l, d.s
  ;Protected.i STime = ElapsedMilliseconds()

  For a = Asc("C") To Asc("Z")
    d = Chr(a) + ":\"
    nfolders = 0
    nused = 0
    nfiles = 0
    b = FindFiles(d)

    If b
; NOTE: Str(nfiles) rather than Str(b) on next line
        Print(Left(d,2)+" has "+Str(nfolders)+" folders\subfolders and "+Str(nfiles))   
        PrintN(" files on it ==> "+cmm(nused)+" bytes.")
    EndIf
  Next

  ;PrintN("Run time " + Str(ElapsedMilliseconds() - STime) + "ms")
EndProcedure

OpenConsole()
;EnableGraphicalConsole(1)
ConsoleTitle("PureBasic - Checking Drives, Folders, Files, and Bytes used.")
;ConsoleColor(15,1)
;ClearConsole()
walkdrives()
PrintN("")
PrintN("All the drives from C: to Z: have been checked.")
While Inkey()=""
  Delay(100)
Wend
End

oldefoxx
Enthusiast
Enthusiast
Posts: 532
Joined: Fri Jul 25, 2003 11:24 pm

Re: learning PureBasic

Post by oldefoxx »

As you can see, I still make use of single character names for numerical values and double
letters for strings. I've no objections to your changing things, nor with you making it all
recursive. Same thing happened quite a few years ago with I wrote a similar piece in
PureBasic to range through all the drives. Why did I write it over again? Because what I
wrote several PB versions back no longer compiles under the newer PB compiler. Rather
than try to fix the old code, I decided to learn it over again.

As to the unused elements in the code I wrote, I wrote it for general use, where you could
drop the Procedures into other code and make use of them. There you would probably want
to know the directory names and the full path to the file in question, plus get the file name
back. It was all in there for a purpose. I just didn't avail myself of all that when I wrote the
calling process to try in illustrate its more general use.

I haven't tried your code yet, but I intend to. I want to see if you have the same results when
it comes to the number of folders and files counted, as well as the number of bytes used. If
your results differ, I am going to try to figure out why.
has-been wanna-be (You may not agree with what I say, but it will make you think).
bosker
Enthusiast
Enthusiast
Posts: 105
Joined: Fri Jan 08, 2010 11:04 pm
Location: Hampshire, UK

Re: learning PureBasic

Post by bosker »

The majority of the changes I made are simply so I could figure out what the thing is doing. Single character names (apart from i, j as loop variables) really make me grumpy. You may of course revert to single character names or whatever else you wish. I also understood that some of the unused things probably had potential uses elsewhere - but when faced with something that doesn't work, simplifying as much as possible is part of a normal approach to debugging.

Of course I tested the code before I posted and it gives results in the same ballpark as the recursive one. Not exactly the same, but close.
Recursive: C: has 35070 folders\subfolders and 286102 files on it ==> 148,057,914,404 bytes.
Modified : C: has 35155 folders\subfolders and 286236 files on it ==> 148,084,443,228 bytes.

I have a number of directories and files imported from Linux that have a '.' as the first character which will be ignored by the recursive program (which is useful for me). A quick count shows that probably accounts for the difference. Near enough for me.
Post Reply