Efficient way to find file with highest number in Delphi
I have a file with a fixed pattern (CONST_) and a running number (XXXX) like this: CONST_XXXX.XYZ.
I am looking for an efficient way to get the file with the highest number in Delphi. Traversing with FindFirst / FindNext seems to be inefficient, if there are many files.
It is well known that finding the maximum of a list, in general, requires all items to be checked. And I believe that the most efficient way to do that is to use FindFirstFile/FindNextFile or related APIs. It's hard to imagine that there will be any real way to improve on the official system API for enumerating files.
That was certainly the opinion offered here: Is there a faster alternative to enumerating folders than FindFirstFile/FindNextFile with C++? Note that I am rejecting out of hand the option of parsing the file system by hand. I don't regard that as being very practical.
On the other hand, this answer offers hope that FindFirstFileEx with FindExInfoBasic and FIND_FIRST_EX_LARGE_FETCH may lead to better performance than plain old FindFirstFile.
You may need to look for an alternative solution to your problem, one that does not involve repeated enumerations of a directory full of files. Perhaps using a database so that you can take advantage of indexing. In fact, it is plausible that the built-in indexing service could be of use.
How about something like this:
for I := 0 to MAX_DIGITS - 4 begin S := 'CONST_' + StringOfChar('0', I); for C := '9' downto '1' do begin if FindFirst(S + C + '*.XYZ', faAnyFile, SearchResult) = 0 then begin //Code to iterate through the results using FindNext //and returning "biggest" Name Result := SearchResult.FileName while FindNext(SearchResult) = 0 //ommitted: handling dirs / hidden if CompareStr(Result, SearchResult.FileName) < 0 then Result := SearchResult.FileName; //adding recursion instead of while... should make it even faster FindClose(SearchResult); Break; end; end; end;
Warning: this code has not been tested
An alternative would be
for I := 9999 downto 0 do begin FileName := Format ('CONST_%.4d.XYZ', [I]); if FileExists(FileName) then Break; end;
Whether this is faster or not depends on the numbers you are expecting and on the performance of FileExists vs. FindFirst which I cannot comment on.
The another way is to read all occurring CONST_*.XYZ into FileListBox and then show the last.
procedure TForm1.Button1Click(Sender: TObject); begin FileListBox1.Directory:='D:\samples'; FileListBox1.Mask:='CONST_*.XYZ'; FileListBox1.Update; Label1.Caption:= FileListBox1.Items[FileListBox1.Items.Count-1]; end;
To make it faster, you can use a function
function getRegion(filestr:string):Boolean; begin if FindFirst(filestr, faAnyFile, searchResult) = 0 then result:=true else result:=false; if result then begin findN:=filestr; end; end; begin SetCurrentDir('D:\samples'); for i:=9 downto 0 do begin if getRegion(Format ('CONST_%.1d*.XYZ', [i])) then break; end; FileListBox1.Directory:='D:\samples'; FileListBox1.Mask:=findN; FileListBox1.Update; Label1.Caption:= FileListBox1.Items[FileListBox1.Items.Count-1];
Update For test A) files were created from 0000-4999 For test B) files were created from 0000-9999 TestA made files from 0000 to only 4999 because user jpfollenius uses downto
from 0000 to 4999 = 5000 files from 9999 downto 4999 = 5000 files
I'm shure with more files 50000 files my solution loads 10000 filenames for example 50000 to 59999 that takes
- moskito-x .................................. 0.345 seconds (tested)
- pure FindFirst / FindNext .......... 0.390 seconds estimated to (0.039 * 10)