2

我在一个目录中有一些文件。我尝试使用 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):integer); 在 FindClose(sr) 之前;

4

3 回答 3

8

正如 jachguate 所说,排序是由 Explorer.exe 完成的,而不是文件系统。FindFirst/FindNext 不保证任何特定的排序,包括基于纯 ASCII 的排序,因此您不应依赖它。但是,您不需要在 Delphi 中重新实现数字排序。Windows 将它使用的那个公开为StrCmpLogicalW,它位于shlwapi.dll. 导入看起来像这样:

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

可以在 Windows 中禁用该行为。如果要遵循 Windows 使用的顺序,则需要使用值调用SHRestrictedREST_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广播消息并在收到消息时重新读取它。

于 2013-01-17T18:26:50.390 回答
1

您在 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;
于 2013-01-17T18:04:11.227 回答
0

通过“排名”,您的意思是排序顺序。

文件以正确的顺序排序(基于字符的 ASCII 值)。2出现在后面19是因为比较仅由两个名称中相同数量的字符组成,并且 '2' 出现在1.

如果您希望它们正确排序为数字,则需要用零填充数字,以便它们的宽度相同(例如SampleFile.2.png,使用代替SampleFile.02.png)。这将导致 '02' 出现在前面,19因此它们按数字正确排序。

您可以使用以下方法解决编号问题:

PngFileName := Format('SampleFile.%.2d.png', [Counter]);
于 2013-01-17T14:20:40.483 回答