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」は。の後に続くため、後になり2ます。191

数字として適切に並べ替える場合は、数字をすべて同じ幅になるようにゼロで左パディングする必要があります(たとえば、の代わりにSampleFile.2.pngを使用しますSampleFile.02.png)。これにより、「02」が前に来る19ので、数値的に正しくソートされます。

次のようなものを使用して、番号付けの問題を修正できます。

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