Thema: Delphi Alle Filenamen

Einzelnen Beitrag anzeigen

Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.691 Beiträge
 
Delphi 11 Alexandria
 
#15

AW: Alle Filenamen

  Alt 14. Mai 2024, 17:40
Falls sich die Anforderungen mal steigern und Du mehr Geschwindigkeit bei der Dateisuche brauchst, das hier habe ich mir mal gebastelt:
Ich kann unter keinen Umständen empfehlen die 2 sub-routinen zu ändern aber der wrapper "FindEx" kann natürlich beliebig umgeschrieben und angepasst werden.
Ein paar Erklärungen sind in Englisch enthalten.
Delphi-Quellcode:
unit uFindEx;

interface

uses
  Winapi.Windows,
  System.SysUtils;

type
  // My variant of an "StringList"
  TFindArray = TArray<string>; // array of WideString;

function FindEx(const ABasePath: string; const AFoldersMustExist: string = ''; AExludedFolders: string = ''; const AFileMask: string = '*.*'; const AIncludeSubFolders: Boolean = False): TFindArray;

implementation

const
  // missing FindEx flags
  FIND_FIRST_EX_CASE_SENSITIVE = $00000001;
  FIND_FIRST_EX_LARGE_FETCH = $00000002;
  FIND_FIRST_EX_ON_DISK_ENTRIES_ONLY = $00000004;

// Small helper to add strings in my "StringList"
procedure AddFindArray(var AFindArray: TFindArray; const AString: string); inline;
var
  i: Integer;
begin
  i := Length(AFindArray);
  SetLength(AFindArray, Succ(i));
  AFindArray[i] := AString;
end;

// This method will crawl thru a folder and collect their names
// The ExclusionList should contain full path names that be total excluded from search, by default everything is included
// IncludeSubFolders switch will get every folder, False by default
// Based upon very fast FindEx Api (Windows)
// The result will contain full path
function FindExFolders(const ABasePath: string = ''; const AExclusionList: TFindArray = []; const AIncludeSubFolders: Boolean = False): TFindArray;
var
  FindExHandle : THandle;
  Win32FindData : TWin32FindDataW;
  FindExInfoLevels: TFindexInfoLevels;
  FindExSearchOps : TFindexSearchOps;
  AdditionalFlags : DWORD;
  tmp : TFindArray;
  i, ii : Integer;
  s:string;
begin
  SetLength(Result, 0);
  if ((ABasePath = '') or (not DirectoryExists(ABasePath))) then
    Exit;
  FindExInfoLevels := FindExInfoBasic;
  FindExSearchOps := FindExSearchLimitToDirectories;
  AdditionalFlags := FIND_FIRST_EX_LARGE_FETCH or FIND_FIRST_EX_ON_DISK_ENTRIES_ONLY;
  FindExHandle := Winapi.Windows.FindFirstFileExW(
                        PWideChar(IncludeTrailingBackslash(ABasePath) + '*.*')
                        ,FindExInfoLevels, @Win32FindData, FindExSearchOps, nil
                        ,AdditionalFlags);
  if (FindExHandle <> INVALID_HANDLE_VALUE) then
    begin
      repeat
        if ((Win32FindData.cFileName <> string('.')) and (Win32FindData.cFileName <> string('..'))
            and (0 <> (Win32FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY))) then
          begin
            if (Length(AExclusionList) > 0) then
              begin
                for i := Low(AExclusionList) to High(AExclusionList) do
                  if (0 = Pos(UpperCase(AExclusionList[i]), UpperCase(IncludeTrailingBackslash(ABasePath) + Win32FindData.cFileName))) then
                    AddFindArray(Result, IncludeTrailingBackslash(ABasePath) + Win32FindData.cFileName);
              end
              else
                AddFindArray(Result, IncludeTrailingBackslash(ABasePath) + Win32FindData.cFileName);
          end;
      until (not Winapi.Windows.FindNextFileW(FindExHandle, Win32FindData));
      Winapi.Windows.FindClose(FindExHandle);
    end;
  if AIncludeSubFolders then
    for i := Low(Result) to High(Result) do
      begin
        tmp := FindExFolders(Result[i], AExclusionList, AIncludeSubFolders);
        for ii := Low(tmp) to High(tmp) do
          AddFindArray(Result, tmp[ii]);
      end;
  SetLength(tmp, 0);
end;

// This method will crawl thru a folder and collect their filenames
// IncludeSubFolders switch will get every filename, False by default
// Based upon very fast FindEx Api (Windows)
// The result will contain full path + filename
function FindExFiles(const ABasePath: string = ''; const AFileMask: string = '*.*'; const AIncludeSubFolders: Boolean = False): TFindArray;
var
  FindExHandle : THandle;
  Win32FindData : TWin32FindDataW;
  FindExInfoLevels: TFindexInfoLevels;
  FindExSearchOps : TFindexSearchOps;
  AdditionalFlags : DWORD;
  tmp, Folders : TFindArray;
  i, ii : Integer;
begin
  SetLength(Result, 0);
  if ((ABasePath = '') or (not DirectoryExists(ABasePath))) then
    Exit;
  SetLength(Folders, 0);
  SetLength(tmp, 0);
  FindExInfoLevels := FindExInfoBasic;
  FindExSearchOps := FindExSearchLimitToDirectories;
  AdditionalFlags := FIND_FIRST_EX_LARGE_FETCH or FIND_FIRST_EX_ON_DISK_ENTRIES_ONLY;
  FindExHandle := Winapi.Windows.FindFirstFileExW(
                        PWideChar(IncludeTrailingBackslash(ABasePath) + AFileMask)
                        ,FindExInfoLevels, @Win32FindData, FindExSearchOps, nil
                        ,AdditionalFlags);
  if (FindExHandle <> INVALID_HANDLE_VALUE) then
    begin
      repeat
        if ((Win32FindData.cFileName <> string('.')) and (Win32FindData.cFileName <> string('..'))) then
          begin
            if (0 = (Win32FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY)) then
              AddFindArray(Result, IncludeTrailingBackslash(ABasePath) + Win32FindData.cFileName);
            if (AIncludeSubFolders and
               (0 <> (Win32FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY))) then
              AddFindArray(Folders, IncludeTrailingBackslash(ABasePath) + Win32FindData.cFileName);
          end;
      until (not Winapi.Windows.FindNextFileW(FindExHandle, Win32FindData));
      Winapi.Windows.FindClose(FindExHandle);
    end;
  if AIncludeSubFolders then
    for i := Low(Folders) to High(Folders) do
      begin
        tmp := FindExFiles(Folders[i], AFileMask, AIncludeSubFolders);
        for ii := Low(tmp) to High(tmp) do
          AddFindArray(Result, tmp[ii]);
      end;
  SetLength(Folders, 0);
  SetLength(tmp, 0);
end;

// My variant of how a file search method can be done for windows systems
// BasePath = where do we start at? eg "C:\Users"
// FoldersMustExist = what foldername is a must for results? eg "Documents", can be left empty for all (seperate with ";" if more than 1)
// ExludedFolders = in what foldername you do not want to search? eg "Documents", can be left empty for all (seperate with ";" if more than 1)
// FileMask = what files you hunt for? eg "*.pas"
// IncludeSubFolders = yes or no, you choose. False by default
// based upon my "FindExFolders" and "FindExFiles" methods
function FindEx(const ABasePath: string; const AFoldersMustExist: string = ''; AExludedFolders: string = ''; const AFileMask: string = '*.*'; const AIncludeSubFolders: Boolean = False): TFindArray;
var
 tmp, Folders, Files: TFindArray;
 splitIncluded, splitExcluded: TFindArray;
 i, ii: Integer;
begin
  SetLength(Result, 0);
  SetLength(tmp, 0);
  SetLength(Folders, 0);
  SetLength(Files, 0);
  SetLength(splitIncluded, 0);
  SetLength(splitExcluded, 0);

  // prepare splittings
  if (Length(AFoldersMustExist) > 0) then
    begin
      if (0 <> Pos(';', AFoldersMustExist)) then
        splitIncluded := AFoldersMustExist.Split([';'])
      else
        AddFindArray(splitIncluded, AFoldersMustExist);
    end;
  if (Length(AExludedFolders) > 0) then
    begin
      if (0 <> Pos(';', AExludedFolders)) then
        splitExcluded := AExludedFolders.Split([';'])
      else
        AddFindArray(splitExcluded, AExludedFolders);
    end;

  // collect folder(s) to work on
  if AIncludeSubFolders then
    tmp := FindExFolders(ABasePath, splitExcluded, AIncludeSubFolders)
    else
    AddFindArray(tmp, ABasePath);

  // sieve out folders that match criteria
  if (Length(splitIncluded) > 0) then
    begin
      for i := Low(tmp) to High(tmp) do
        for ii := Low(splitIncluded) to High(splitIncluded) do
          if (0 <> Pos(UpperCase(splitIncluded[ii]), UpperCase(tmp[i]))) then
            AddFindArray(Folders, tmp[i]);
    end
    else
      Folders := tmp;

  // get files that matching the criteria
  for i := Low(Folders) to High(Folders) do
    begin
      if (Length(AFileMask) > 0) then
        tmp := FindExFiles(Folders[i], AFileMask) // do not enable the IncludeSubFolders switch here (!)
      else
        tmp := FindExFiles(Folders[i]);
      for ii := Low(tmp) to High(tmp) do
        AddFindArray(Files, tmp[ii]);
    end;

  Result := Files;
  SetLength(tmp, 0);
  SetLength(Folders, 0);
  SetLength(Files, 0);
  SetLength(splitIncluded, 0);
  SetLength(splitExcluded, 0);
end;

end.
Gruß vom KodeZwerg
  Mit Zitat antworten Zitat