FindFirst, FindNext (Delphi Xe, Win7) рейтинг не является правильным

У меня есть несколько файлов в каталоге. Я пытаюсь получить эти файлы с помощью FindFirst и FindNext, но не могу получить тот же порядок в Windows 7.

C:\Test
SampleFile.0.png
SampleFile.1.png
SampleFile.2.png
SampleFile.3.png
SampleFile.4.png
SampleFile.5.png
SampleFile.6.png
SampleFile.7.png
SampleFile.8.png
SampleFile.9.png
SampleFile.10.png
SampleFile.11.png
SampleFile.12.png
SampleFile.13.png
SampleFile.14.png
SampleFile.15.png
SampleFile.16.png
SampleFile.17.png
SampleFile.18.png
SampleFile.19.png
SampleFile.20.png
SampleFile.21.png
SampleFile.22.png

Когда я пытаюсь использовать свой код, у меня есть

SampleFile.0.png
SampleFile.1.png
SampleFile.10.png
SampleFile.11.png
SampleFile.12.png
SampleFile.13.png
SampleFile.14.png
SampleFile.15.png
SampleFile.16.png
SampleFile.17.png
SampleFile.18.png
SampleFile.19.png
SampleFile.2.png
SampleFile.20.png
SampleFile.21.png
.
.
.

Как я могу получить список файлов в правильном порядке ранга?

Procedure Test;
var
sr : TSearchRec;
i : integer;
ListFiles : TStringList;  
begin
ListFiles := TStringList.Create;
i := FindFirst('c:\test\*.png', faDirectory, sr);
while i = 0 do begin  
ListFiles.Add(ExtractFileName(sr.FindData.cFileName));
i := FindNext(sr); 
end;
FindClose(sr);
end;  

Примечание: результат по-прежнему неверен, если я могу использовать ListFiles.Sorted = True


Я думаю, что у меня есть решение, создал функцию.

function SortFilesByName(List: TStringList; Index1, Index2: Integer): integer;
var
FileName1, FileName2: String;
i, FileNumber1, FileNumber2: Integer;
begin
  FileName1 := ChangeFileExt(ExtractFileName(List[Index1]), '');
  FileName2 := ChangeFileExt(ExtractFileName(List[Index2]), '');
  i := POS('.', FileName1)+1;
  FileNumber1 := StrToInt(Copy(FileName1, i, MaxInt));
  i := POS('.', FileName2)+1;
  FileNumber2 := StrToInt(Copy(FileName2, i, MaxInt));
  Result := (FileNumber1 - FileNumber2);
end;

Я добавил еще одну строку ListFiles.CustomSort(SortFilesByName); //(ListFiles,1,2): целое число); перед FindClose(sr);

3 ответа

Как сказал jachguate, сортировка выполняется Explorer.exe, а не файловой системой. FindFirst/FindNext не гарантирует какой-либо конкретной сортировки, в том числе простой ASCII на основе, поэтому вы не должны полагаться на нее. Однако вам не нужно повторно реализовывать числовую сортировку в Delphi. Windows выставляет тот, который он использует как StrCmpLogicalW, который находится в shlwapi.dll, Импорт выглядит так:

function StrCmpLogicalW(psz1, psz2: PWideChar): Integer; stdcall;
  external 'shlwapi.dll'

Это можно отключить в Windows. Если вы хотите следовать порядку, который использует Windows, вам нужно вызвать SHRestricted с REST_NOSTRCMPLOGICAL значение. Если он возвращает true, вам следует использовать AnsiCompareStr.

const
  // Use default CompareString instead of StrCmpLogical
  REST_NOSTRCMPLOGICAL = $4000007E;

function SHRestricted(rest: DWORD): LongBool; stdcall; external 'shell32.dll';

Итак, ваша последняя функция сортировки должна выглядеть примерно так:

function CompareFilenames(const AFilename1, AFilename2: string): Integer;
begin
  if SHRestricted(REST_NOSTRCMPLOGICAL) then
    Result := AnsiCompareStr(AFilename1, AFilename2)
  else
    Result := StrCmpLogicalW(PWideChar(AFilename1), PWideChar(AFilename2));
end;

Вы можете кэшировать результат вызова SHRestricted, но если вам нужно, вы должны следить за WM_SETTINGSCHANGE широковещательное сообщение и перечитайте его, когда получите.

Различные порядки, которые вы видите в проводнике Windows, реализованы в explorer.exe, а не в файловой системе.

Числовой порядок сортировки - это новая функция в Windows 7, поэтому, если вы сортируете по имени и у вас есть куча файлов с префиксом, за которым следуют цифры, проводник "идентифицирует" этот шаблон и не представляет список, отсортированный по имени в традиционный способ, но отсортированный по префиксу, а затем по номеру (как если бы строка была целым числом).

Если вы хотите сделать то же самое в Delphi, вы можете сделать это, добавив все имена файлов, возвращаемые FindFirst/FindNext, в TSlist, а затем отсортируйте список строк, используя эту функцию сравнения:

var
  FileNames: TList<string>;
begin
  FileNames := TList<string>.Create;
  try
    SearchForFiles(FileNames); //here you add all the file names
    //sort file names a la windows 7 explorer
    FileNames.Sort(System.Generics.Defaults.TComparer<string>.Construct(
      function (const s1, s2: string): Integer
        procedure ProcessPrefix(const fn: string; var prefix, number: string);
        var
          I: Integer;
        begin
          for I := length(fn) downto 1 do
            if not TCharacter.IsDigit(fn[I]) then
            begin
              Prefix := Copy(fn, 1, I);
              number := Copy(fn, I+1, MaxInt);
              Break;
            end;
        end;
      var
        prefix1, prefix2: string;
        number1, number2: string;
        fn1, fn2: string;
      begin
        //compare filenames a la windows 7 explorer
        fn1 := TPath.GetFileNameWithoutExtension(s1);
        fn2 := TPath.GetFileNameWithoutExtension(s2);
        ProcessPrefix(fn1, prefix1, number1);
        ProcessPrefix(fn2, prefix2, number2);
        if (Number1 <> '') and (Number2 <> '') then
        begin
          Result := CompareText(prefix1, prefix2);
          if Result = 0 then
            Result := CompareValue(StrToInt(number1), StrToInt(Number2));
        end
        else
          Result := CompareText(s1, s2);
      end
      ));
    UseYourSortedFileNames(FileNames);
  finally
    FileNames.Free;
  end;
end;

Под "рангом" вы подразумеваете порядок сортировки.

Файлы сортируются в правильном порядке (на основе значения символов ASCII). 2 идет после 19 потому что сравнение выполняется только с одинаковым количеством символов в обоих именах, а после 2 1,

Если вы хотите, чтобы они правильно сортировались в виде чисел, вам нужно дополнить цифры нулями слева, чтобы они имели одинаковую ширину (например, вместо SampleFile.2.pngиспользовать SampleFile.02.png). Это заставит '02' прийти раньше 19 поэтому они сортируют правильно численно.

Вы можете исправить проблему с нумерацией, используя что-то вроде:

PngFileName := Format('SampleFile.%.2d.png', [Counter]);
Другие вопросы по тегам