FindFirst,FindNext(Delphi Xe,Win7)排名不正确

时间:2013-01-17 11:20:15

标签: delphi delphi-xe rank findfirst

我在目录中有一些文件。我尝试使用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 个答案:

答案 0 :(得分:7)

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

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

可以在Windows中禁用该行为。如果您想按照Windows使用的顺序,则需要使用REST_NOSTRCMPLOGICAL值来调用SHRestricted。如果它返回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广播消息并在获得时重新阅读。

答案 1 :(得分:0)

“排名”是指排序顺序。

文件按正确顺序排序(基于字符的ASCII值)。 2位于19之后,因为比较仅在两个名称中都包含相同数量的字符,“{2}”位于1之后。

如果你希望它们作为数字正确排序,你需要用零填充数字,这样它们的宽度都相同(例如,而不是SampleFile.2.png,使用SampleFile.02.png) 。这将导致'02'在19之前到来,因此它们在数字上正确排序。

您可以使用以下内容修复编号问题:

PngFileName := Format('SampleFile.%.2d.png', [Counter]);

答案 2 :(得分:0)

您在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;