Sorting Dir() result by date-time attribute
Sorting Dir() result by date-time attribute
Hi,
I got to list files in directory using Dir(), but I still cannot determine the sorting criteria, so I would like to know it there is a way to define the sorting to creation/modification date both ascendent or descendent.
Many thanks.
I got to list files in directory using Dir(), but I still cannot determine the sorting criteria, so I would like to know it there is a way to define the sorting to creation/modification date both ascendent or descendent.
Many thanks.
-
- Posts: 2958
- Joined: Jun 02, 2015 16:24
Re: Sorting Dir() result by date-time attribute
Hi Julcar,
Are you on Windows?
Maybe you can use a Winapi function strategy then. Here a snippet, inspired by here : http://forums.codeguru.com/showthread.p ... Time-order :
I know it may look a little busy stuff if not too much used to it. But actually I find it useful because you get access to the information you need about the datetimes stamps in a structured way here in the FILETIME structures:
I haven't sorted the results however. I think we can use also Quicksort from CRT.BI, like in the example of codeguru.
The results shows like that:
Are you on Windows?
Maybe you can use a Winapi function strategy then. Here a snippet, inspired by here : http://forums.codeguru.com/showthread.p ... Time-order :
Code: Select all
#include "windows.bi"
dim as const string searchDir = "C://*.*"
dim as WIN32_FIND_DATA fdata(any)
dim as WIN32_FIND_DATA fd
dim as HANDLE hFind = FindFirstFile(searchDir, @fd)
while (INVALID_HANDLE_VALUE <> hFind)
redim preserve fdata(ubound(fdata) + 1)
fdata(ubound(fdata)) = fd
dim as boolean res = FindNextFile(hFind, @fd)
if (not res) then
FindClose(hFind)
exit while
end if
wend
dim as SYSTEMTIME st
for i as integer = 0 to ubound(fdata)
? fdata(i).cFileName,
FileTimeToSystemTime(@fdata(i).ftCreationTime, @st)
? st.wYear; ":"; st.wMonth; ":"; st.wDayOfWeek
next i
sleep
making it easy to sort out.https://docs.microsoft.com/en-us/window ... find_dataa
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
I haven't sorted the results however. I think we can use also Quicksort from CRT.BI, like in the example of codeguru.
The results shows like that:
ProgramData 2019:3:2
Python27 2020:1:5
Python377 2020:4:1
Qt 2019:12:4
Recovery 2018:7:2
swapfile.sys 2019:6:6
System Volume Information 2018:7:2
tmp 2018:12:1
Users 2019:3:2
vcredist.bmp 2007:11:3
VC_RED.cab 2007:11:3
VC_RED.MSI 2007:11:3
Re: Sorting Dir() result by date-time attribute
I doubt there is a built-in FB command such as SortFiles, but you can use the Shell with dir /O* /T*:
Recall.bi (loads a file from disk into a string array):
Code: Select all
#include "Recall.bi"
Shell("dir /O-D *.bas >tmpdir.txt")
Dim As string s()
Dim As integer elements=Recall("tmpdir.txt", s())
For ct As Integer=5 to UBound(s)-4
Print ct, s(ct)
Next
sleep
Code: Select all
' Recall.bi, 15 September 2020, jj2007
#include once "crt.bi" ' needed for memcpy
#ifndef maxCell
#Define maxCell 100 ' whatever you consider enough for a single cell
#endif
Dim shared retstr As string * maxCell+1
Function Recall(fname As String, locArray() As String) As Integer
Dim As Integer ct=0, cursize=1000 ' locArray is a local representation of a dynamic array
Dim As Long f=Freefile, sBytes
If Open(fname For Binary Access Read As #f) = 0 Then
Dim as ubyte ptr pContent = Allocate(Lof(f)), CurPos=pContent, CrPos
Dim as long flen=Lof(f)
Get #f, 1, *pContent, Lof(f)
Close #f
Do
if ct=0 or ct>cursize then
cursize+=cursize shr 1
ReDim Preserve locArray(cursize)
endif
CrPos=strstr(CurPos, Chr(10))
sBytes=CrPos-CurPos-1
if CrPos=0 Then CrPos=pContent+flen:flen=0:sBytes=CrPos-CurPos
if sBytes>0 then
locArray(ct)=Space(sBytes)
memcpy(StrPtr(locArray(ct)), CurPos, sBytes)
endif
CurPos=CrPos+1
ct+=1
Loop until flen=0
While ct>1 and len(trim(locArray(ct-2)))=0 ' get rid of trailing empty strings
ct-=1
Wend
ReDim Preserve locArray(ct)
DeAllocate(pContent)
Else
Print "Error opening file"
End If
Return ct
End Function
Function Cell(row As integer, col As integer, locArray() As String) As string
Dim As integer ct=0, ctTabs=0, posLeft=-1, posRight=0
Dim As ubyte ptr pString
Dim c As ubyte
pString=StrPtr(locArray(row))
if pString then
Do
c=pString[ct]
if c=0 then
if ctTabs>=col then posRight=ct+1
Exit do
endif
if c=9 then
ctTabs=ctTabs+1
if col=0 then
posLeft=0
if ctTabs>col then
posRight=ct+1
Exit do
endif
else
if posLeft=-1 and ctTabs>=col then
posLeft=ct+1
elseif ctTabs>col then
posRight=ct+1
Exit do
endif
endif
endif
ct=ct+1
Loop
endif
if posRight=0 then
retstr[0]=0
else
posRight-=posLeft
if posRight>maxCell then posRight=maxCell
memcpy(StrPtr(retstr), pString+posLeft, posRight)
retstr[posRight-1]=0
endif
return retstr
end function
So you missed the most exciting part ;-)Tourist Trap wrote:I haven't sorted the results however
-
- Posts: 2958
- Joined: Jun 02, 2015 16:24
Re: Sorting Dir() result by date-time attribute
When it comes to Quicksort I leave it to dodicat :)jj2007 wrote:So you missed the most exciting part ;-)
edit: Ok you are right jj2007, job is unfinished so in an ultimate effort here is my try with the crt QSort in continuation of my previous snippet:
Code: Select all
#include "windows.bi"
#include "crt/stdlib.bi"
dim as const string searchDir = "C://*.*"
dim as WIN32_FIND_DATA fdata(any)
dim as WIN32_FIND_DATA fd
dim as HANDLE hFind = FindFirstFile(searchDir, @fd)
while (INVALID_HANDLE_VALUE <> hFind)
redim preserve fdata(ubound(fdata) + 1)
fdata(ubound(fdata)) = fd
dim as boolean res = FindNextFile(hFind, @fd)
if (not res) then
FindClose(hFind)
exit while
end if
wend
dim as SYSTEMTIME st
? "unsorted*********************************************************************"
for i as integer = 0 to ubound(fdata)
? fdata(i).cFileName,
FileTimeToSystemTime(@fdata(i).ftCreationTime, @st)
? st.wYear; ":"; st.wMonth; ":"; st.wDayOfWeek
next i
? "sorted*********************************************************************"
function compareFdata(byref fd1 as WIN32_FIND_DATA, byref fd2 as WIN32_FIND_DATA) as integer
dim as SYSTEMTIME st1, st2
FileTimeToSystemTime(@fd1.ftCreationTime, @st1)
FileTimeToSystemTime(@fd2.ftCreationTime, @st2)
'
dim as single stCompareTime1, stCompareTime2
stCompareTime1 = st1.wYear*100 + st1.wMonth + st1.wDayOfWeek/100
stCompareTime2 = st2.wYear*100 + st2.wMonth + st2.wDayOfWeek/100
'
return stCompareTime1 - stCompareTime2
end function
qsort(@fdata(0), ubound(fdata) + 1, sizeOf(WIN32_FIND_DATA), @compareFdata)
for i as integer = 0 to ubound(fdata)
? fdata(i).cFileName, ,
FileTimeToSystemTime(@fdata(i).ftCreationTime, @st)
? st.wYear; ":"; st.wMonth; ":"; st.wDayOfWeek
next i
sleep
(by the way I don't understand why I need to do "ubound(fdata) + 1" in the QSort, it's above the items count it seems?)
Re: Sorting Dir() result by date-time attribute
I know, I did the exercise for SortFiles ;-)Tourist Trap wrote:I need some coffee now ;)
With *.bas from my FB folder, I get a crash (FB GAS, also Gcc32). It runs fine with FB64, though.
Besides, I wonder if wDayOfWeek will give you the desired result: that is 0=Sunday, 1=Monday etc; you might use wDay instead. Still, many will want also hour, minutes and seconds. For SortFiles (written in Assembly) I used the FILETIME member of the WIN32_FIND_DATA structure directly.
-
- Posts: 2958
- Joined: Jun 02, 2015 16:24
Re: Sorting Dir() result by date-time attribute
Thanks for pointing this out, I guess I've missed some cast between long and integer somewhere. - I used FB64.jj2007 wrote: With *.bas from my FB folder, I get a crash (FB GAS, also Gcc32). It runs fine with FB64, though.
The FILETIME member is already in the good numeric form?jj2007 wrote: Besides, I wonder if wDayOfWeek will give you the desired result: that is 0=Sunday, 1=Monday etc; you might use wDay instead. Still, many will want also hour, minutes and seconds. For SortFiles (written in Assembly) I used the FILETIME member of the WIN32_FIND_DATA structure directly.
Re: Sorting Dir() result by date-time attribute
Welllllll... it's a QWORD. No problem in Assembly, but I can't speak for FB. It resembles vaguely a double, you could try that road ;-)Tourist Trap wrote:The FILETIME member is already in the good numeric form?
-
- Posts: 2958
- Joined: Jun 02, 2015 16:24
Re: Sorting Dir() result by date-time attribute
QWORD seems to be a kind of 64bits storage in 4 (quad) parts holding 16bits. I'll take a look at it.jj2007 wrote:Welllllll... it's a QWORD. No problem in Assembly, but I can't speak for FB. It resembles vaguely a double, you could try that road ;-)Tourist Trap wrote:The FILETIME member is already in the good numeric form?
About the crash in FB32, seems to be the problem of the UBOUND mentionned earlier. If I use the correct UBOUND - 1 (in the QSORT), I have one item unsorted... If I do UBOUND + 1 so that everything gets sorted, it crashes.
Re: Sorting Dir() result by date-time attribute
Qsort expects a c-style function, i.e. not StdCall. Here is one that works fine with FB64 and FB32:Tourist Trap wrote:About the crash in FB32
Code: Select all
function compareFdata cdecl (byref fd1 as WIN32_FIND_DATA, byref fd2 as WIN32_FIND_DATA) as LONGINT
Dim As integer temp, pFD1=@fd1.ftCreationTime, pFD2=@fd2.ftCreationTime
Dim As single multX=1.0e-8
asm
#ifdef __FB_64BIT__
mov rax, [pFD2]
fild QWORD PTR [rax]
mov rax, [pFD1]
fild QWORD PTR [rax]
fsub
fmul DWORD PTR [multX] ' get rid of nanoseconds
fistp DWORD PTR [temp]
#else
mov eax, [pFD2]
fild QWORD PTR [eax]
mov eax, [pFD1]
fild QWORD PTR [eax]
fsub
fmul DWORD PTR [multX] ' get rid of nanoseconds
fistp DWORD PTR [temp]
#endif
end asm
return temp 'stCompareTime1 - stCompareTime2
end function
Re: Sorting Dir() result by date-time attribute
Code: Select all
function compareFdata cdecl (byref fd1 as WIN32_FIND_DATA, byref fd2 as WIN32_FIND_DATA) as LONGINT
...
Re: Sorting Dir() result by date-time attribute
Does anybody know a decent way to display the files correctly? I have struggled with LOCATE 10, crslin (doesn't work), so I use LSET; second problem is how to display the time correctly, e.g. as 10:05 (not 10: 5):
Code: Select all
qsort(@fdata(0), ubound(fdata) + 1, sizeOf(WIN32_FIND_DATA), @compareFdata)
Dim as string fname=Space(40)
for i as integer = 0 to ubound(fdata)
if i<9 or i>ubound(fdata)-9 Then
Lset fname=fdata(i).cFileName
FileTimeToSystemTime(@fdata(i).ftCreationTime, @st)
? fname; " ";st.wDay;".";st.wMonth;".";st.wYear,
? using " ##:##"; st.wHour; st.wMinute
elseif i=9 Then
Print "..."
endif
next i
sleep
-
- Posts: 2958
- Joined: Jun 02, 2015 16:24
Re: Sorting Dir() result by date-time attribute
I struggled also. Couldn't find the print using option to write a 0 when needed, so had to tweak everything by hand:jj2007 wrote:Does anybody know a decent way to display the files correctly?
Code: Select all
#include "windows.bi"
#include "crt/stdlib.bi"
dim as const string searchDir = "C://*.*"
dim as WIN32_FIND_DATA fdata(any)
dim as WIN32_FIND_DATA fd
dim as HANDLE hFind = FindFirstFile(searchDir, @fd)
while (INVALID_HANDLE_VALUE <> hFind)
redim preserve fdata(ubound(fdata) + 1)
fdata(ubound(fdata)) = fd
dim as boolean res = FindNextFile(hFind, @fd)
if (not res) then
FindClose(hFind)
exit while
end if
wend
? searchDir
?
dim as SYSTEMTIME st
? "unsorted*********************************************************************"
for i as integer = 0 to ubound(fdata)
print iif(len(fdata(i).cFileName)>20, "..", "") & right(fdata(i).cFileName, 20);
FileTimeToSystemTime(@fdata(i).ftCreationTime, @st)
locate csrlin, 30 : ? "0";
locate csrlin, 30 + 2 - len(trim(str(st.wDay))): ? left(trim(str(st.wDay)),2);
locate csrlin, 33 : ? "0";
locate csrlin, 33 + 2 - len(trim(str(st.wMonth))) : ? left(trim(str(st.wMonth)),2);
locate csrlin, 36 : ? "0";
locate csrlin, 36 + 4 - len(trim(str(st.wYear))) : ? left(trim(str(st.wYear)),4);
locate csrlin, 42 : ? "0";
locate csrlin, 42 + 2 - len(trim(str(st.wHour))) : ? left(trim(str(st.wHour)),2) & ":";
locate csrlin, 45 : ? "0";
locate csrlin, 45 + 2 - len(trim(str(st.wMinute))) : ? left(trim(str(st.wMinute)),2)
next i
?
? "sorted*********************************************************************"
function compareFdata CDECL (byref fd1 as WIN32_FIND_DATA, byref fd2 as WIN32_FIND_DATA) as LONGINT
Dim As integer temp, pFD1=@fd1.ftCreationTime, pFD2=@fd2.ftCreationTime
Dim As single multX=1.0e-8
asm
#ifdef __FB_64BIT__
mov rax, [pFD2]
fild QWORD PTR [rax]
mov rax, [pFD1]
fild QWORD PTR [rax]
fsub
fmul DWORD PTR [multX] ' get rid of nanoseconds
fistp DWORD PTR [temp]
#else
mov eax, [pFD2]
fild QWORD PTR [eax]
mov eax, [pFD1]
fild QWORD PTR [eax]
fsub
fmul DWORD PTR [multX] ' get rid of nanoseconds
fistp DWORD PTR [temp]
#endif
end asm
return temp 'stCompareTime1 - stCompareTime2
end function
qsort(@fdata(0), ubound(fdata), sizeOf(WIN32_FIND_DATA), @compareFdata)
for i as integer = 0 to ubound(fdata)- 1
print iif(len(fdata(i).cFileName)>20, "..", "") & right(fdata(i).cFileName, 20);
FileTimeToSystemTime(@fdata(i).ftCreationTime, @st)
locate csrlin, 30 : ? "0";
locate csrlin, 30 + 2 - len(trim(str(st.wDay))): ? left(trim(str(st.wDay)),2);
locate csrlin, 33 : ? "0";
locate csrlin, 33 + 2 - len(trim(str(st.wMonth))) : ? left(trim(str(st.wMonth)),2);
locate csrlin, 36 : ? "0";
locate csrlin, 36 + 4 - len(trim(str(st.wYear))) : ? left(trim(str(st.wYear)),4);
locate csrlin, 42 : ? "0";
locate csrlin, 42 + 2 - len(trim(str(st.wHour))) : ? left(trim(str(st.wHour)),2) & ":";
locate csrlin, 45 : ? "0";
locate csrlin, 45 + 2 - len(trim(str(st.wMinute))) : ? left(trim(str(st.wMinute)),2)
next i
?
? "done"
sleep
(some spaces are not displayed here, but it should be aligned in a good way)ProgramData 19 03 2019 04:52
Program Files 19 03 2019 04:52
Users 19 03 2019 04:37
Windows 19 03 2019 04:37
tmp 31 12 2018 19:54
Microsoft 27 11 2018 15:36
..cuments and Settings 24 11 2018 20:18
Recovery 03 07 2018 22:28
..m Volume Information 03 07 2018 22:23
devlist.txt 03 07 2018 15:08
Finish.log 03 07 2018 15:08
Intel 03 07 2018 14:33
eSupport 20 11 2017 02:10
$Recycle.Bin 29 09 2017 13:46
VC_RED.MSI 07 11 2007 07:12
VC_RED.cab 07 11 2007 07:09
install.res.1040.dll 07 11 2007 07:03
install.res.2052.dll 07 11 2007 07:03
Re: Sorting Dir() result by date-time attribute
Thanks for the replys 0_o hehe
In Windows I use this batch file which I call with SHELL and let me save the listing in a file in disk sorted by creation date
In Windows I use this batch file which I call with SHELL and let me save the listing in a file in disk sorted by creation date
Code: Select all
@echo off
setlocal
set cnt=0
for /f "delims=" %%A in ('DIR %1 /B /L /O:-D /T:C %2') do (
echo %%A
set /a "cnt+=1, 1/(cnt%%%3)" 2>nul || goto :break
)
:break
-
- Posts: 2958
- Joined: Jun 02, 2015 16:24
Re: Sorting Dir() result by date-time attribute
Hi Julcar, great batch file. Unfortunately I can't decypher it. I just see the %1 that means you need to provide a command argument to the batch file. For the rest, I don't see what is %2, and so on, or any of the switches of DIR (maybe /B sounds familiar).Julcar wrote:In Windows I use this batch file which I call with SHELL and let me save the listing in a file in disk sorted by creation dateCode: Select all
@echo off setlocal set cnt=0 for /f "delims=" %%A in ('DIR %1 /B /L /O:-D /T:C %2') do ( echo %%A set /a "cnt+=1, 1/(cnt%%%3)" 2>nul || goto :break ) :break
If you could split it in a more managable way, we could certainly send it as a string to the SHELL function of freebasic. As it, me at least, I'm stuck.
Re: Sorting Dir() result by date-time attribute
Try this one.
Code: Select all
#include "crt.bi"
#define up <,>
#define down >,<
#macro SetQsort(datatype,fname,b1,b2,dot)
Sub fname(array() As datatype,begin As Long,Finish As Ulong)
Dim As Long i=begin,j=finish
Dim As datatype x =array(((I+J)\2))
While I <= J
While array(I)dot b1 X dot:I+=1:Wend
While array(J)dot b2 X dot:J-=1:Wend
If I<=J Then Swap array(I),array(J): I+=1:J-=1
Wend
If J > begin Then fname(array(),begin,J)
If I < Finish Then fname(array(),I,Finish)
End Sub
#endmacro
Declare Function stats Cdecl Alias "_stat"(As zstring Ptr,As Any Ptr) As Integer
Enum choice
LastAccessed=1
LastModified
Created
Size
Alphabetical
End Enum
Type info
As String g
As stat s
End Type
Sub string_split(Byval s As String,chars As String,result() As String)
Redim result(0)
Dim As String var1,var2
Dim As Long pst,LC=Len(chars)
#macro split(stri)
pst=Instr(stri,chars)
var1="":var2=""
If pst<>0 Then
var1=Mid(stri,1,pst-1)
var2=Mid(stri,pst+LC)
Else
var1=stri
End If
If Len(var1) Then
Redim Preserve result(1 To Ubound(result)+1)
result(Ubound(result))=var1
End If
#endmacro
Do
split(s):s=var2
Loop Until var2=""
End Sub
Function isfolder(path As zstring Ptr) As Long
#define S_ISDIR(m) (((m) And &hF000) = &h4000)
Dim As stat statbuf
If (stats(path, @statbuf) <> 0) Then Return 0
Return S_ISDIR(statbuf.st_mode)
End Function
Function isfile(fname As String) As boolean
Return Iif(isfolder(fname),0,1)
End Function
Function pipeout(Byval s As String="") Byref As String
Var f=Freefile
Dim As String tmp
Open Pipe s For Input As #f
s=""
Do Until Eof(f)
Line Input #f,tmp
s+=tmp+Chr(10)
Loop
Close #f
Return s
End Function
Dim As String s= pipeout("dir /b")
Redim As String temp()
string_split(s,Chr(10),temp())
SetQsort(info,sortaccessed,up,.s.st_atime)
SetQsort(info,sortmodified,up,.s.st_mtime)
SetQsort(info,sortcreatedtime,up,.s.st_ctime)
SetQsort(info,sortsize,up,.s.st_size)
SetQsort(info,sortalphabetical,up,.g)
Redim As String path()
Dim As Long counter
For n As Long=Lbound(temp) To Ubound(temp)
If isfile(temp(n)) Then
counter+=1
Redim Preserve path(1 To counter)
path(counter)=temp(n)
End If
Next
Dim As info statbuf(Lbound(path) To Ubound(path))
Dim As stat buffer
For n As Long =Lbound(statbuf) To Ubound(statbuf)
statbuf(n).g=lcase(path(n))
stats(path(n),@buffer)
statbuf(n).s.st_atime=buffer.st_atime
statbuf(n).s.st_mtime=buffer.st_mtime
statbuf(n).s.st_ctime=buffer.st_ctime
statbuf(n).s.st_size=buffer.st_size
Next n
Dim As long c
start:
Print "Number of files in this folder = ";Ubound(statbuf)
Print "Please choose to view:"
Print "1 = LastAccessed"
Print "2 = LastModified"
Print "3 = Created"
Print "4 = Size"
Print "5 = Alphabetical"
Print "Any other key to end"
Print "type in your choice"
c=Val(Input(1))
print "______________________________"
Dim As tm Ptr timeinfo
Dim As zstring * 80 buff
Select Case c
Case LastAccessed
sortaccessed(statbuf(),Lbound(statbuf),Ubound(statbuf))
Print "file";Tab(50);"Last accessed"
print
For n As Long=Lbound(statbuf) To Ubound(statbuf)
timeinfo=localtime(@statbuf(n).s.st_atime)
strftime(buff, Sizeof(buff), "%b %d %H:%M %Y", timeinfo)
Print statbuf(n).g;Tab(50);buff
Next
Case LastModified
sortmodified(statbuf(),Lbound(statbuf),Ubound(statbuf))
Print "file";Tab(50);"Last modified"
print
For n As Long=Lbound(statbuf) To Ubound(statbuf)
timeinfo=localtime(@statbuf(n).s.st_mtime)
strftime(buff, Sizeof(buff), "%b %d %H:%M %Y", timeinfo)
Print statbuf(n).g;Tab(50);buff
Next
Case created
sortcreatedtime(statbuf(),Lbound(statbuf),Ubound(statbuf))
Print "file";Tab(50);"Created"
print
For n As Long=Lbound(statbuf) To Ubound(statbuf)
timeinfo=localtime(@statbuf(n).s.st_ctime)
strftime(buff, Sizeof(buff), "%b %d %H:%M %Y", timeinfo)
Print statbuf(n).g;Tab(50);buff
Next
Case size
sortsize(statbuf(),Lbound(statbuf),Ubound(statbuf))
Print "file";Tab(50);"size -bytes"
print
For n As Long=Lbound(statbuf) To Ubound(statbuf)
Print statbuf(n).g;Tab(50);statbuf(n).s.st_size
Next n
Case Alphabetical
sortalphabetical(statbuf(),Lbound(statbuf),Ubound(statbuf))
print "List alphabetically"
print
For n As Long=Lbound(statbuf) To Ubound(statbuf)
Print statbuf(n).g
Next n
Case Else
End
End Select
print
print
goto start
Sleep