Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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

enter image description here

Testtable TestA

enter image description here

Test B

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)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top