Sorting output of Dir()

New to FreeBASIC? Post your questions here.
fzabkar
Posts: 178
Joined: Sep 29, 2018 2:52
Location: Australia

Sorting output of Dir()

Post by fzabkar »

Is there any way to retrieve file names in "hexadecimally" sorted order using Dir()?

This is the (dis)ordered set produced by "dir /on" at the Windows 10 command prompt:

Code: Select all

102.rpm
103.rpm
104.rpm
105.rpm
106.rpm
108.rpm
109.rpm
11.rpm
1100.rpm
1101.rpm
1121.rpm
115.rpm
116.rpm
118.rpm
119.rpm
120.rpm
1205.rpm
121.rpm
122.rpm
123.rpm
124.rpm
125.rpm
126.rpm
127.rpm
128.rpm
12A.rpm
12B.rpm
12D.rpm
12E.rpm
132.rpm
13F5.rpm
147.rpm
149.rpm
14A.rpm
1500.rpm
1501.rpm
1502.rpm
1503.rpm
1504.rpm
1505.rpm
1506.rpm
1507.rpm
1508.rpm
1509.rpm
150A.rpm
150B.rpm
150C.rpm
150D.rpm
150E.rpm
150F.rpm
16F.rpm
1903.rpm
191.rpm
1B.rpm
fzabkar
Posts: 178
Joined: Sep 29, 2018 2:52
Location: Australia

Re: Sorting output of Dir()

Post by fzabkar »

I propose to count the files with an initial Dir() loop, then Redim a string array, FNam, with N elements, where N is the number of files. Then I Redim a UShort array with N elements, FNum, to store the hex values of the corresponding filenames, plus an additional Index array where Index(N) = N.

I then propose to bubble sort FNum, swapping the Index in line with Fnum. I can then process the files using the sort order in Fnum, and use the Index to retrieve the file name from Fnam. This avoids moving strings around in memory, which I would think would be computationally intensive.

Does that sound like a reasonable plan, or is there an easier alternative?
SMC
Posts: 33
Joined: Feb 22, 2012 4:05

Re: Sorting output of Dir()

Post by SMC »

fzabkar wrote: Apr 06, 2025 20:35 I propose to count the files with an initial Dir() loop, then Redim a string array, FNam, with N elements, where N is the number of files. Then I Redim a UShort array with N elements, FNum, to store the hex values of the corresponding filenames, plus an additional Index array where Index(N) = N.

I then propose to bubble sort FNum, swapping the Index in line with Fnum. I can then process the files using the sort order in Fnum, and use the Index to retrieve the file name from Fnam. This avoids moving strings around in memory, which I would think would be computationally intensive.

Does that sound like a reasonable plan, or is there an easier alternative?
That's the only way I can think of it being done. I don't really understand how Dir() orders files normally. It seems like it might be newest first or something. That's what my test program is showing at least.
fzabkar
Posts: 178
Joined: Sep 29, 2018 2:52
Location: Australia

Re: Sorting output of Dir()

Post by fzabkar »

SMC wrote: Apr 07, 2025 4:35That's the only way I can think of it being done. I don't really understand how Dir() orders files normally. It seems like it might be newest first or something. That's what my test program is showing at least.
Thanks. I've gone ahead and done it, and it works.
D.J.Peters
Posts: 8642
Joined: May 28, 2005 3:28
Contact:

Re: Sorting output of Dir()

Post by D.J.Peters »

Here are how you can sort an array with hex file names.

Joshy

Code: Select all

dim as string folder(...)=>{"c12.rpm","6.rpm","1c26.rpm","a1.rpm","12c.rpm","2.rpm","b12.rpm","22.rpm"}

sub sorthex(a() as string)
  var i=0,j=0,bDoit=true
  var s = lbound(a), e = ubound(a)
  if e-s<2 then return
  var bSort = true
  while bDoit
    bDoit = false
    for i as integer = s to e-1
      j=i+1 : if val("&H" & a(i)) > val("&H" & a(j)) then swap a(i),a(j) : bDoit = true
    next  
  wend
end sub
? "unsorted"
for i as integer = lbound(folder) to ubound(folder)
  print chr(34) & folder(i) & chr(34)
next
?:? "sorted"
sorthex(folder())
for i as integer = lbound(folder) to ubound(folder)
  print chr(34) & folder(i) & chr(34)
next
sleep
here are with the array from dir() command:

Code: Select all

sub sorthex(a() as string)
  var i=0,j=0,bDoit=true
  var s = lbound(a), e = ubound(a)
  if e-s<2 then return
  var bSort = true
  while bDoit
    bDoit = false
    for i as integer = s to e-1
      j=i+1 : if val("&H" & a(i)) > val("&H" & a(j)) then swap a(i),a(j) : bDoit = true
    next  
  wend
end sub

var oldFolder = curdir() 
var curFolder = exepath()
chdir curFolder

redim as string folder(any)
var nFiles = 0,i = 0
var sFile = dir("*.rpm")
while len(sFile)
  redim preserve folder(nFiles) 
  folder(nFiles) = sFile : nFiles+=1
  sFile = dir()  
wend
if nFiles<2 then 
  ? "number of files: " & nFiles & " nothing to sort!"
  beep : sleep :end 1
endif  
chdir oldFolder

? "unsorted"
for i = 0 to nFiles-1
  print chr(34) & folder(i) & chr(34)
next
?:? "sorted"
sorthex(folder())
for i = 0 to nFiles-1
  print chr(34) & folder(i) & chr(34)
next
sleep[/code}
fzabkar
Posts: 178
Joined: Sep 29, 2018 2:52
Location: Australia

Re: Sorting output of Dir()

Post by fzabkar »

This is what I did.

Code: Select all

Dim sFNam() As String
Dim wdFNum() As UShort
Dim wdIndx() As UShort
Dim wdNumFils As UShort
Dim bSorted As UByte                                    ' = 1 when bubble sort is complete
Dim sDir As String = "some_test_dir"
Dim i As Integer


' count number of files in directory

wdNumFils = 0

If Dir( sDir & "*.*" ) <> "" Then

    wdNumFils = 1

    While Dir() <> ""

        wdNumFils += 1

    Wend

End If

Redim  sFNam( 1 To wdNumFils ) As String
Redim wdFNum( 1 To wdNumFils ) As UShort
Redim wdIndx( 1 To wdNumFils ) As UShort


' get each file name and build arrays

sFNam( 1 ) = Dir( sDir & "*.*" )
wdFNum( 1 ) = Val( "&h" & Left( sFNam( 1 ), 4 ) )
wdIndx( 1 ) = 1


For i = 2 To wdNumFils

   sFNam( i ) = Dir()
  wdFNum( i ) = Val( "&h" & Left( sFNam( i ), 4 ) )
  wdIndx( i ) = i

Next i

' bubble sort Fnum

Do
    bSorted = 1
   
    For i = 1 To wdNumFils - 1
       
        If wdFNum( i ) > wdFNum( i + 1 ) Then
           
            Swap wdFNum( i ), wdFNum( i + 1 )
            Swap wdIndx( i ), wdIndx( i + 1 )
           
            bSorted = 0
       
        End If
     
    Next i
         
Loop While bSorted = 0
UEZ
Posts: 1079
Joined: May 05, 2017 19:59
Location: Germany

Re: Sorting output of Dir()

Post by UEZ »

I think you need some kind of natural sort order:

Code: Select all

'code by UEZ build 2025-04-08 alpha
#include "crt/stdlib.bi"

Type tTemp
    num As Integer
    str As String
End Type

Function StrComp(a As String, b As String, mode As UByte = 0) As Byte
    If mode = 1 Then
        a = LCase(a)
        b = LCase(b)
    End If
    
    If a < b Then Return -1
    If a > b Then Return 1
    Return 0
End Function

'comparison function for qsort
Function CompareWrapperInt cdecl(a As Any Ptr, b As Any Ptr) As Integer
    Dim As tTemp Ptr arrA = CPtr(tTemp Ptr, a)
    Dim As tTemp Ptr arrB = CPtr(tTemp Ptr, b)
    
    'first compare the num column (Integer)
    If arrA->num < arrB->num Then Return -1
    If arrA->num > arrB->num Then Return 1
    
    'if the numbers are the same, compare the str column (String)
    Return StrComp(arrA->str, arrB->str)
End Function


Sub NaturalSort(arr() As String)
	'create a temporary array of type tTemp to store num and str
	Dim As tTemp arr2d(UBound(arr))
	Dim As UInteger i
	
	'populate the temporary array with the length of each string (num) and the string itself (str)
	For i = 0 To UBound(arr)
		arr2d(i).num = Len(arr(i))
		arr2d(i).str = arr(i)
	Next
	
	'perform sorting using qsort based On CompareWrapperInt Function
	qsort(@arr2d(0), UBound(arr) + 1, SizeOf(tTemp), CPtr(Any Ptr, @CompareWrapperInt))
	
	'after sorting, copy the sorted strings back to the original array
	For i = 0 To UBound(arr)
		arr(i) = arr2d(i).str
	Next
End Sub


Dim As String arr(0 To ...) = {"103.rmp", "102.rmp", "1903.rmp", "106.rmp", "105.rmp", "108.rmp", "109.rmp", "1101.rmp", "1121.rmp", "115.rmp", "116.rmp", "118.rmp", "1504.rmp", "1100.rmp", _
                               "120.rmp", "1205.rmp", "121.rmp", "122.rmp", "123.rmp", "124.rmp", "125.rmp", "126.rmp", "127.rmp", "128.rmp", "150D.rmp", "12E.rmp", "12B.rmp", "12A.rmp", "11.rmp", _
                               "13F5.rmp", "147.rmp", "149.rmp", "14A.rmp", "1501.rmp", "1502.rmp", "1503.rmp", "119.rmp", "1505.rmp", "1506.rmp", "1507.rmp", "1508.rmp", "1509.rmp", "1500.rmp", _
                               "150B.rmp", "150C.rmp", "150E.rmp", "150F.rmp", "16F.rmp", "104.rmp", "191.rmp", "1B.rmp", "12D.rmp", "132.rmp", "150A.rmp"}
Dim As Integer count = UBound(arr), i

? "Array elements = " & count + 1

NaturalSort(arr())

For i = 0 To UBound(arr)
	? arr(i)
Next

Sleep
This should produce this output:

Code: Select all

Array elements = 54
11.rmp
1B.rmp
102.rmp
103.rmp
104.rmp
105.rmp
106.rmp
108.rmp
109.rmp
115.rmp
116.rmp
118.rmp
119.rmp
120.rmp
121.rmp
122.rmp
123.rmp
124.rmp
125.rmp
126.rmp
127.rmp
128.rmp
12A.rmp
12B.rmp
12D.rmp
12E.rmp
132.rmp
147.rmp
149.rmp
14A.rmp
16F.rmp
191.rmp
1100.rmp
1101.rmp
1121.rmp
1205.rmp
13F5.rmp
1500.rmp
1501.rmp
1502.rmp
1503.rmp
1504.rmp
1505.rmp
1506.rmp
1507.rmp
1508.rmp
1509.rmp
150A.rmp
150B.rmp
150C.rmp
150D.rmp
150E.rmp
150F.rmp
1903.rmp
Last edited by UEZ on Apr 08, 2025 20:26, edited 2 times in total.
fzabkar
Posts: 178
Joined: Sep 29, 2018 2:52
Location: Australia

Re: Sorting output of Dir()

Post by fzabkar »

@UEZ, your program crashes on my 32-bit Win 10 box.

Edit:

The program runs if I comment out the qsort line.
UEZ
Posts: 1079
Joined: May 05, 2017 19:59
Location: Germany

Re: Sorting output of Dir()

Post by UEZ »

You are right, it runs properly when compiled as x64. At the moment I cannot find the bug why it doesn't work with x86!

Error

Code: Select all

Aborting due to runtime error 12 ("segmentation violation" signal) in c:\...\Test03.bas::NATURALSORT()


Edit: found the issue -> cdecl was not added in Function CompareWrapperInt (a As Any Ptr, b As Any Ptr) As Integer.

Correct:

Code: Select all

Function CompareWrapperInt cdecl(a As Any Ptr, b As Any Ptr) As Integer
I updated the code from my previous post.
Last edited by UEZ on Apr 08, 2025 18:45, edited 1 time in total.
fzabkar
Posts: 178
Joined: Sep 29, 2018 2:52
Location: Australia

Re: Sorting output of Dir()

Post by fzabkar »

Thanks @UEZ. The code works now.
dodicat
Posts: 8267
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Sorting output of Dir()

Post by dodicat »

Another method, (with a macro to save typing).

Code: Select all

Dim As String s
#macro settostring(z...)
s=#z
#endmacro

settostring(_
102.rpm,_
103.rpm,_
104.rpm,_
105.rpm,_
106.rpm,_
108.rpm,_
109.rpm,_
11.rpm,_
1100.rpm,_
1101.rpm,_
1121.rpm,_
115.rpm,_
116.rpm,_
118.rpm,_
119.rpm,_
120.rpm,_
1205.rpm,_
121.rpm,_
122.rpm,_
123.rpm,_
124.rpm,_
125.rpm,_
126.rpm,_
127.rpm,_
128.rpm,_
12A.rpm,_
12B.rpm,_
12D.rpm,_
12E.rpm,_
132.rpm,_
13F5.rpm,_
147.rpm,_
149.rpm,_
14A.rpm,_
1500.rpm,_
1501.rpm,_
1502.rpm,_
1503.rpm,_
1504.rpm,_
1505.rpm,_
1506.rpm,_
1507.rpm,_
1508.rpm,_
1509.rpm,_
150A.rpm,_
150B.rpm,_
150C.rpm,_
150D.rpm,_
150E.rpm,_
150F.rpm,_
16F.rpm,_
1903.rpm,_
191.rpm,_
1B.rpm)
print "Original"
Print s

Sub split(Byval stri As String,char As String=",",g() As String)
    Dim As Long c
    start: c+=1
    Var pst=Instr(stri,char),var1="",var2=""
    If pst<>0 Then
        var1=Mid(stri,1,pst-1)
        var2=Mid(stri,pst+1)
    Else
        var1=stri
    End If
    Redim Preserve g(1 To c)
    g(c)=var1
    stri=var2
    If Len(var2) Then Goto start
End Sub

Sub bubblesortHEX(g() As String)
    For n1 As Long=Lbound(g) To Ubound(g)-1
        For n2 As Long=n1+1 To Ubound(g)
            If Valint("&h"+g(n1)) > Valint("&h"+g(n2))Then Swap g(n1),g(n2)
        Next
    Next
End Sub



Redim As String g()
split(s,,g())

bubblesortHEX(g())
Print
Print"Sorted hex"
For n As Long=Lbound(g) To Ubound(g)
    Print g(n);iif(n<ubound(g),",","");
Next
Sleep
 
UEZ
Posts: 1079
Joined: May 05, 2017 19:59
Location: Germany

Re: Sorting output of Dir()

Post by UEZ »

D.J.Peters wrote: Apr 08, 2025 4:04 Here are how you can sort an array with hex file names.

Joshy

Code: Select all

dim as string folder(...)=>{"c12.rpm","6.rpm","1c26.rpm","a1.rpm","12c.rpm","2.rpm","b12.rpm","22.rpm"}

sub sorthex(a() as string)
  var i=0,j=0,bDoit=true
  var s = lbound(a), e = ubound(a)
  if e-s<2 then return
  var bSort = true
  while bDoit
    bDoit = false
    for i as integer = s to e-1
      j=i+1 : if val("&H" & a(i)) > val("&H" & a(j)) then swap a(i),a(j) : bDoit = true
    next  
  wend
end sub
? "unsorted"
for i as integer = lbound(folder) to ubound(folder)
  print chr(34) & folder(i) & chr(34)
next
?:? "sorted"
sorthex(folder())
for i as integer = lbound(folder) to ubound(folder)
  print chr(34) & folder(i) & chr(34)
next
sleep
Your code fails with

Code: Select all

Dim As String folder(...) => {"z12.rpm", "6.rpm", "1c26.rpm", "z.rpm", "12c.rpm", "2.rpm", "b12.rpm", "22.rpm"}
Output:

Code: Select all

sorted
"z12.rpm"
"z.rpm"
"2.rpm"
"6.rpm"
"22.rpm"
"12c.rpm"
"b12.rpm"
"1c26.rpm"
dodicat wrote: Apr 09, 2025 9:39 Another method, (with a macro to save typing).
I cannot compile it -> error in lines 67, 69, 70, 72, 75, 76
dodicat
Posts: 8267
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Sorting output of Dir()

Post by dodicat »

Hi UEZ.
It is OK here, win 11.
I have no compiler flags used, i.e. a raw compile 32 and 64 bit with the latest official build- 1.10.1
My results:

Code: Select all

Original
102.rpm,103.rpm,104.rpm,105.rpm,106.rpm,108.rpm,109.rpm,11.rpm,1100.rpm,1101.rpm,1121.rpm,115.rpm,116.rpm,118.rpm,119.rpm,120.rpm,1205.rpm,121.rpm,122.rpm,123.rpm,124.rpm,125.rpm,126.rpm,127.rpm,128.rpm,12A.rpm,12B.rpm,12D.rpm,12E.rpm,132.rpm,13F5.rpm,147.rpm,149.rpm,14A.rpm,1500.rpm,1501.rpm,1502.rpm,1503.rpm,1504.rpm,1505.rpm,1506.rpm,1507.rpm,1508.rpm,1509.rpm,150A.rpm,150B.rpm,150C.rpm,150D.rpm,150E.rpm,150F.rpm,16F.rpm,1903.rpm,191.rpm,1B.rpm

Sorted hex
11.rpm,1B.rpm,102.rpm,103.rpm,104.rpm,105.rpm,106.rpm,108.rpm,109.rpm,115.rpm,116.rpm,118.rpm,119.rpm,120.rpm,121.rpm,122.rpm,123.rpm,124.rpm,125.rpm,126.rpm,127.rpm,128.rpm,12A.rpm,12B.rpm,12D.rpm,12E.rpm,132.rpm,147.rpm,149.rpm,14A.rpm,16F.rpm,191.rpm,1100.rpm,1101.rpm,1121.rpm,1205.rpm,13F5.rpm,1500.rpm,1501.rpm,1502.rpm,1503.rpm,1504.rpm,1505.rpm,1506.rpm,1507.rpm,1508.rpm,1509.rpm,150A.rpm,150B.rpm,150C.rpm,150D.rpm,150E.rpm,150F.rpm,1903.rpm 
Try this split procedure:

Code: Select all

Sub split(Byval stri As String,char As String=",",g() As String)
    Dim As Long c,l=len(char)
    do
     c+=1
    Var pst=Instr(stri,char),var1="",var2=""
    If pst<>0 Then
        var1=Mid(stri,1,pst-1)
        var2=Mid(stri,pst+l)
    Else
        var1=stri
    End If
    Redim Preserve g(1 To c)
     g(c)=var1
     if len(var1)=0 then c-=1
    stri=var2
    if len(var2)=0 then exit do
    loop 
End Sub 
Where do you get the z from in D.J.Peter's code?
z12.rpm
UEZ
Posts: 1079
Joined: May 05, 2017 19:59
Location: Germany

Re: Sorting output of Dir()

Post by UEZ »

dodicat wrote: Apr 10, 2025 0:17 Hi UEZ.
It is OK here, win 11.
I have no compiler flags used, i.e. a raw compile 32 and 64 bit with the latest official build- 1.10.1
My results:

Code: Select all

Original
102.rpm,103.rpm,104.rpm,105.rpm,106.rpm,108.rpm,109.rpm,11.rpm,1100.rpm,1101.rpm,1121.rpm,115.rpm,116.rpm,118.rpm,119.rpm,120.rpm,1205.rpm,121.rpm,122.rpm,123.rpm,124.rpm,125.rpm,126.rpm,127.rpm,128.rpm,12A.rpm,12B.rpm,12D.rpm,12E.rpm,132.rpm,13F5.rpm,147.rpm,149.rpm,14A.rpm,1500.rpm,1501.rpm,1502.rpm,1503.rpm,1504.rpm,1505.rpm,1506.rpm,1507.rpm,1508.rpm,1509.rpm,150A.rpm,150B.rpm,150C.rpm,150D.rpm,150E.rpm,150F.rpm,16F.rpm,1903.rpm,191.rpm,1B.rpm

Sorted hex
11.rpm,1B.rpm,102.rpm,103.rpm,104.rpm,105.rpm,106.rpm,108.rpm,109.rpm,115.rpm,116.rpm,118.rpm,119.rpm,120.rpm,121.rpm,122.rpm,123.rpm,124.rpm,125.rpm,126.rpm,127.rpm,128.rpm,12A.rpm,12B.rpm,12D.rpm,12E.rpm,132.rpm,147.rpm,149.rpm,14A.rpm,16F.rpm,191.rpm,1100.rpm,1101.rpm,1121.rpm,1205.rpm,13F5.rpm,1500.rpm,1501.rpm,1502.rpm,1503.rpm,1504.rpm,1505.rpm,1506.rpm,1507.rpm,1508.rpm,1509.rpm,150A.rpm,150B.rpm,150C.rpm,150D.rpm,150E.rpm,150F.rpm,1903.rpm 
Try this split procedure:

Code: Select all

Sub split(Byval stri As String,char As String=",",g() As String)
    Dim As Long c,l=len(char)
    do
     c+=1
    Var pst=Instr(stri,char),var1="",var2=""
    If pst<>0 Then
        var1=Mid(stri,1,pst-1)
        var2=Mid(stri,pst+l)
    Else
        var1=stri
    End If
    Redim Preserve g(1 To c)
     g(c)=var1
     if len(var1)=0 then c-=1
    stri=var2
    if len(var2)=0 then exit do
    loop 
End Sub 
Where do you get the z from in D.J.Peter's code?
z12.rpm
I changed the lines to

Code: Select all

    
    Dim As Short pst = InStr(stri, char)
    Dim As String var1 = "", var2 = ""
and no compiler errors.

Anyway, I added the string for testing purposes. With your code it fails, but without the added strings it runs correctly. The prefix of the strings *.rpm does only contain hex values and thus it will fail for non hex chars. For this purpose your hex compare is ok.

Just wondering why I writing this at all...
dodicat
Posts: 8267
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Sorting output of Dir()

Post by dodicat »

OK- UEZ
I assumed that all .rpm should be hex files.
Any non hex will have a val of 0, thus appear at the beginning after a sort.
But it is strange that the var things don't work with you.
eg.

Code: Select all

 dim as long c
 start: c+=1
 Var pst=Instr("FREEBASIC","BA"),var1="a",var2="b"
 print pst,var1,var2,c
 if c<10 then goto start
 sleep
  
PS.
"Just wondering why I writing this at all..."
I am a bit zombified just now, my old border collie Bob died on Monday, he was about 15, but it is so sad to say goodbye forever.
Post Reply