0

私は降伏し、欲しいものを手に入れるためにほぼ12時間費やしますが、できません。

このコードはすべてのフォルダーとファイル名を検索しますが、検索から除外したいフォルダーのサブディレクトリを含むいくつかのフォルダーを除外したいです。

助けてくれる人がいればいいのに。

procedure TForm1.CombineDir(InDir : string; OutStream : TStream);
var  AE : TArchiveEntry;
     dFound:boolean;

  procedure RecurseDirectory(ADir : string);
  var  sr : TSearchRec;
       TmpStream : TStream;
  begin
    if FindFirst(ADir + '*', faAnyFile, sr) = 0 then begin
      repeat
        if (sr.Attr and (faDirectory or faVolumeID)) = 0 then begin
          //ShowMessage('Filename is :>'+ ADir + sr.Name);
          if (NotThisPath.IndexOf(ADir + sr.Name)>=0) or dFound then begin
            ShowMessage('DO NOT INCLUDE THIS FILENAME :>'+ ADir + sr.Name);
          end else begin
            ShowMessage('>>> INCLUDE THIS FILENAME :>'+ ADir + sr.Name);
            // We have a file (as opposed to a directory or anything
            // else). Write the file entry header.
            AE.EntryType := aeFile;
            AE.FileNameLen := Length(sr.Name);
            AE.FileLength := sr.Size;
            OutStream.Write(AE, SizeOf(AE));
            OutStream.Write(sr.Name[1], Length(sr.Name));
            // Write the file itself
            TmpStream := TFileStream.Create(ADir + sr.Name, fmOpenRead or fmShareDenyWrite);
            OutStream.CopyFrom(TmpStream, TmpStream.Size);
            TmpStream.Free;
          end;
        end;

        if (sr.Attr and faDirectory) > 0 then begin
          if (sr.Name <> '.') and (sr.Name <> '..') then begin
            //ShowMessage('DIR is:>'+ ADir + sr.Name);
            //if (Pos(ADir, NotThisPath.Text)>0) then
            if (NotThisPath.IndexOf(ADir + sr.Name)>=0) then begin
              ShowMessage('DO NOT INCLUDE THIS DIR:>'+ ADir + sr.Name);
              dFound:=True;
            end else begin
              ShowMessage('>>> INCLUDE THIS DIR:>'+ ADir + sr.Name);
              // Write the directory entry
              AE.EntryType := aeDirectory;
              AE.DirNameLen := Length(sr.Name);
              OutStream.Write(AE, SizeOf(AE));
              OutStream.Write(sr.Name[1], Length(sr.Name));
            end;
            // Recurse into this directory
            RecurseDirectory(IncludeTrailingPathDelimiter(ADir + sr.Name));
          end;
        end;
      until FindNext(sr) <> 0;
      FindClose(sr);
    end;
    // Show that we are done with this directory
    AE.EntryType := aeEOD;
    OutStream.Write(AE, SizeOf(AE));
  end;

begin
RecurseDirectory(IncludeTrailingPathDelimiter(InDir));
end;

NotThisPath は TStringList です。

4

1 に答える 1

5

あなたの根本的な問題は、ファイルの列挙、ファイル名のフィルタリング、および GUI を 1 つの不潔な塊に混ぜ合わせたことだと思います。FindFirstフォームのメソッドから呼び出されていることを確認しないでください。呼び出すコードFindFirstは、ヘルパー クラスまたは関数に属します。

あなたの質問に直接答えようとするつもりはありません。特に、あなたが実際に質問していないからです。私が試みようとしているのは、ファイルの列挙と名前のフィルタリングの問題を分離する方法を示すことです。

まず、次の関数を実装します。

procedure EnumerateFiles(Dir: string; 
  const EnumerateFileName: TEnumerateFileNameMethod);

この関数は、Dirパラメーターでディレクトリを渡され、そのディレクトリ内のすべてのファイル、そのサブディレクトリなどを再帰的に列挙します。見つかった各ファイルは、コールバック メソッドに渡されますEnumerateFileName。これは次のように定義されます。

type
  TEnumerateFileNameMethod = procedure(const FileName: string) of object;

実装は確かに非常に簡単です。これは、標準FindFirstベースの繰り返しループです。.この関数は、特別なディレクトリおよびを拒否し..ます。遭遇したすべてのディレクトリに再帰します。

procedure EnumerateFiles(Dir: string;
  const EnumerateFileName: TEnumerateFileNameMethod);
var
  SR: TSearchRec;
begin
  Dir := IncludeTrailingPathDelimiter(Dir);
  if FindFirst(Dir + '*', faAnyFile, SR) = 0 then
    try
      repeat
        if (SR.Name = '.') or (SR.Name = '..') then
          continue;
        if (SR.Attr and faDirectory) <> 0 then
          EnumerateFiles(Dir + SR.Name, EnumerateFileName)
        else
          EnumerateFileName(Dir + SR.Name);
      until FindNext(SR) <> 0;
    finally
      FindClose(SR);
    end;
end;

さて、これは従うのに十分簡単なはずです。次の問題はフィルタリングです。提供するコールバック メソッドでそれを実装できます。.pasこれは、拡張子を持つ Delphi ソース ファイルを選択するフィルタリングを示す完全なデモです。

program EnumerateFilesDemo;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TEnumerateFileNameMethod = procedure(const FileName: string) of object;

procedure EnumerateFiles(Dir: string;
  const EnumerateFileName: TEnumerateFileNameMethod);
var
  SR: TSearchRec;
begin
  Dir := IncludeTrailingPathDelimiter(Dir);
  if FindFirst(Dir + '*', faAnyFile, SR) = 0 then
    try
      repeat
        if (SR.Name = '.') or (SR.Name = '..') then
          continue;
        if (SR.Attr and faDirectory) <> 0 then
          EnumerateFiles(Dir + SR.Name, EnumerateFileName)
        else
          EnumerateFileName(Dir + SR.Name);
      until FindNext(SR) <> 0;
    finally
      FindClose(SR);
    end;
end;

type
  TDummyClass = class
    class procedure EnumerateFileName(const FileName: string);
  end;

class procedure TDummyClass.EnumerateFileName(const FileName: string);
begin
  if SameText(ExtractFileExt(FileName), '.pas') then
    Writeln(FileName);
end;

procedure Main;
begin
  EnumerateFiles('C:\Users\heff\Development', TDummyClass.EnumerateFileName);
end;

begin
  try
    Main;
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

これはあなたがやりたいフィルタリングのタイプではないことはわかっていますが、要点は、今では一般性があるということです。SameTextへの呼び出しを、必要なフィルタリングに置き換えることができます。処理したいファイルを選択したら、好きなように処理できます。

便宜上、クラスメソッドを使用しました。オブジェクトをインスタンス化する定型文をデモに詰め込みたくありませんでした。ただし、必要に応じて、列挙コールバックを処理するクラスを作成する必要があります。そのクラスは、実行中のファイル アーカイブ操作をカプセル化します。そのクラスは、出力ストリームのインスタンスを所有します。コールバック メソッドは、アーカイブに書き込むインスタンス メソッドになります。

今、私はあなたの問題に対する完全な解決策を実装していませんが、もっと良いことをしたことを願っています. つまり、コードを因数分解して問題を簡単に解決する方法を示すことです。

于 2013-07-31T20:05:29.020 に答える