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]);