Delphi TClientDataSet和删除集合中的文件

Delphi TClientDataSet和删除集合中的文件,delphi,delphi-xe7,Delphi,Delphi Xe7,我有一个我正在开发的实验应用程序,它将图像文件名添加到集合中。我试图找到最有效的方法来删除集合中的所有文件,除了 对于存在于另一个集合中的文件。文件可以存在于任何集合中 我有一个包含以下字段的TClientDataSet: ClientDataSet1.FieldDefs.Add('Index', ftInteger); ClientDataSet1.FieldDefs.Add('Collection', ftString, 50); ClientDataSet1.FieldDefs.Add('

我有一个我正在开发的实验应用程序,它将图像文件名添加到集合中。我试图找到最有效的方法来删除集合中的所有文件,除了 对于存在于另一个集合中的文件。文件可以存在于任何集合中

我有一个包含以下字段的TClientDataSet:

ClientDataSet1.FieldDefs.Add('Index', ftInteger);
ClientDataSet1.FieldDefs.Add('Collection', ftString, 50);
ClientDataSet1.FieldDefs.Add('Filename', ftString, 254);

I came up with this which seems to work but seems inefficient:
var
  i: Integer;
  j: Integer;
  iCollectionToDelete: string;
  iCollection: string;
  iFilename: string;
  iFilenameInOtherCollection: string;
  iFilesInOtherCollectionsStringList: TStringList;
begin
  iCollectionToDelete := ListBox1.Items[ListBox1.ItemIndex];
  { Set filtered to false to see all the records in the database }
  ClientDataSet1.Filtered := False;
  { Create a list of files not in the collection to be deleted }
  iFilesInOtherCollectionsStringList := TStringList.Create;
  try
    for i := 0 to ClientDataSet1.RecordCount - 1 do
    begin
       iCollection := ClientDataSet1.FieldByName('Collection').AsString;
       iFilename := ClientDataSet1.FieldByName('Filename').AsString;
       if iCollection <> iCollectionToDelete then
       begin
          iFilenameInOtherCollection := ClientDataSet1.FieldByName('Filename').AsString;
          iFilesInOtherCollectionsStringList.Add(iFilename);
       end;
       ClientDataSet1.Next;
    end;

    { Compare the iFilenameInOtherCollection with all the filenames in the
      dataset and if the iFilename is not in the other collection then
      erase the file }
    ClientDataSet1.First;
    for i := 0 to ClientDataSet1.RecordCount - 1 do
    begin
       iFilename := ClientDataSet1.FieldByName('Filename').AsString;
       ClientDataSet1.Next;
       for j := 0 to iFilesInOtherCollectionsStringList.Count - 1 do
       begin
          iFilenameInOtherCollection := iFilesInOtherCollectionsStringList[j];
          if iFilesInOtherCollectionsStringList.IndexOf(iFilename) = -1 then
            if FileExists(iFilename) then
             WindowsErase(handle, iFilename, False, False, False, False);
       end;
    end;
  finally
    iFilesInOtherCollectionsStringList.Free;
  end;
end;
ClientDataSet1.FieldDefs.Add('Index',ftInteger);
ClientDataSet1.FieldDefs.Add('Collection',ftString,50);
ClientDataSet1.FieldDefs.Add('Filename',ftString,254);
我想出了一个似乎有效但效率低下的方法:
变量
i:整数;
j:整数;
iCollectionToDelete:字符串;
i集合:字符串;
iFilename:字符串;
iFilenameInOtherCollection:字符串;
IflesinotherCollectionsStringList:TStringList;
开始
iCollectionToDelete:=ListBox1.Items[ListBox1.ItemIndex];
{将筛选设置为false以查看数据库中的所有记录}
ClientDataSet1.Filtered:=False;
{创建不在要删除的集合中的文件列表}
iFilesInOtherCollectionsStringList:=TStringList.Create;
尝试
对于i:=0到ClientDataSet1.RecordCount-1 do
开始
iCollection:=ClientDataSet1.FieldByName('Collection').AsString;
iFilename:=ClientDataSet1.FieldByName('Filename').AssString;
如果iCollection iCollectionToDelete,则
开始
iFilenameInOtherCollection:=ClientDataSet1.FieldByName('Filename').AssString;
iFilesInOtherCollectionsStringList.Add(iFilename);
结束;
ClientDataSet1.Next;
结束;
{将iFilenameInOtherCollection与
如果iFilename不在其他集合中,则
删除文件}
ClientDataSet1.First;
对于i:=0到ClientDataSet1.RecordCount-1 do
开始
iFilename:=ClientDataSet1.FieldByName('Filename').AssString;
ClientDataSet1.Next;
对于j:=0到iFilesInOtherCollectionsStringList.Count-1 do
开始
iFilenameInOtherCollection:=iFilesInOtherCollectionsStringList[j];
如果iFilesInOtherCollectionsStringList.IndexOf(iFilename)=-1,则
如果文件存在(iFilename),则
WindowsErase(句柄、iFilename、False、False、False、False);
结束;
结束;
最后
iFilesInOtherCollectionsStringList.Free;
结束;
结束;
我的问题是,这能提高效率吗?还是有办法做到同样的事情
仅使用TClientDataset方法?

在填充后,只需添加
iFilesInOtherCollectionsStringList.Sorted:=True
IndexOf
然后将使用快速二进制搜索,而不是极其缓慢的逐个循环。也许这对你来说就足够了


另一个选项是先准备要删除的列表,然后启动一个工作线程,该线程将在后台执行删除。这可能会有所帮助,因为文件操作通常比内存比较慢得多。您可以通过注释掉
WindowsErase
行来检查是否是删除减慢了您的过程。

只是为了好玩,我想我应该尝试不使用StringList,而是使用ClientDataSet的一些功能,即在一条语句中过滤和将数据从一张CD复制到另一张CD的能力。它比使用StringList要短得多,因此可能更容易维护/修改/重构

我还没有将其与Stringlist版本进行基准测试,但如果速度更快,我会感到惊讶, 因为它依赖于TClientDataSet.Locate,即使在处理索引字段时,这也不是一个特别有效的操作

代码如下。希望这些评论能解释它是如何工作的

procedure TForm1.SetUp;
begin
  ClientDataSet1.FieldDefs.Add('Index', ftInteger);
  ClientDataSet1.FieldDefs.Add('Collection', ftString, 50);
  ClientDataSet1.FieldDefs.Add('Filename', ftString, 254);
  ClientDataSet1.CreateDataSet;

  // Create some test data
  ClientDataSet1.InsertRecord([1, 'C1', 'F1']);
  ClientDataSet1.InsertRecord([2, 'C2', 'F1']);
  ClientDataSet1.InsertRecord([3, 'C3', 'F1']);
  ClientDataSet1.InsertRecord([4, 'C1', 'F2']);
  ClientDataSet1.InsertRecord([5, 'C3', 'F3']);
end;


procedure Tform1.ApplyCDSFilter(CDS : TClientDataSet; FilterExpr : String);
// utility routine to filter/unfilter a dataset
begin
  CDS.Filtered := False;
  CDS.Filter := FilterExpr;
  if FilterExpr <> '' then
    CDS.Filtered := True;
end;

procedure TForm1.RemoveFilesOnlyInCollection(CollectionName : String);
var
  CDS : TClientDataSet;
  FilterExpr : String;
  AFileName : String;
begin
  // In the following, I'm just going to add the names of the files which belong to the
  // specified collection as well as to another one
  // to a listbox so as to be able to check the results by inspection

  Listbox1.Items.Clear;

  // next create a temporary CDS
  CDS := TClientDataSet.Create(Nil);

  // Index it by Filename
  CDS.IndexFieldNames := 'Filename';

  // Copy the data from ClientDataSet1 into it
  CDS.Data := ClientDataSet1.Data;

  // Construct a filter expression to select the collection whose members are to be
  // retained.  NOTE : the QuotedStr is to handle quotes embedded in the collection name.
  FilterExpr := '(Collection =' + QuotedStr(CollectionName) + ')';

  //  Apply the filter to ClientDataSet1, so that only records that contain the CollectionName 
  //  are "visible", temporarily
  ApplyCDSFilter(ClientDataSet1, FilterExpr);

  //  Next, negate the filter expression and apply it to the temporary CDS
  FilterExpr := 'not ' + FilterExpr;
  ApplyCDSFilter(CDS, FilterExpr);

  //  Now, we can loop through ClientDataSet1 and test whether the Filename is present
  //  in the temporary CDS.  If it is, that means that the Filename belongs to another  
  // collection too.

  try
    ClientDataSet1.DisableControls;
    ClientDataSet1.First;
    while not ClientDataSet1.Eof do begin
      AFileName := ClientDataSet1.FieldByName('Filename').AsString;
      if not CDS.Locate('Filename', AFileName, [loCaseInsensitive]) then
        Listbox1.Items.Add(AFileName);
      ClientDataSet1.Next;
    end;
   finally
     CDS.Free;
     ClientDataSet1.EnableControls;
     ApplyCDSFilter(ClientDataSet1, '');
   end;
end;
程序TForm1.SetUp;
开始
ClientDataSet1.FieldDefs.Add('Index',ftInteger);
ClientDataSet1.FieldDefs.Add('Collection',ftString,50);
ClientDataSet1.FieldDefs.Add('Filename',ftString,254);
ClientDataSet1.CreateDataSet;
//创建一些测试数据
ClientDataSet1.InsertRecord([1',C1',F1']);
ClientDataSet1.InsertRecord([2',C2',F1']);
ClientDataSet1.InsertRecord([3',C3',F1']);
ClientDataSet1.InsertRecord([4',C1',F2']);
ClientDataSet1.InsertRecord([5',C3',F3']);
结束;
过程Tform1.ApplyCDSFilter(CDS:TClientDataSet;FilterExpr:String);
//用于筛选/取消筛选数据集的实用程序例程
开始
CDS.Filtered:=False;
CDS.Filter:=FilterExpr;
如果FilterExpr为“”,则
CDS.Filtered:=True;
结束;
过程TForm1.RemoveFilesOnlyCollection(CollectionName:String);
变量
CDS:TClientDataSet;
FilterExpr:字符串;
AFileName:String;
开始
//在下面,我将添加属于
//指定的集合以及另一个集合
//到列表框,以便能够通过检查检查结果
Listbox1.Items.Clear;
//接下来创建一个临时CD
CDS:=TClientDataSet.Create(Nil);
//按文件名编制索引
CDS.IndexFieldNames:=“文件名”;
//将数据从ClientDataSet1复制到其中
CDS.Data:=ClientDataSet1.Data;
//构造筛选器表达式以选择要删除其成员的集合
//保留。注意:QuotedStr用于处理集合名称中嵌入的引号。
FilterExpr:='(集合='+QuotedStr(集合名称)+');
//将筛选器应用于ClientDataSet1,以便仅允许包含CollectionName的记录
//暂时是“可见”的
ApplyCDSFilter(ClientDataSet1,FilterExpr);
//接下来,对过滤器表达式求反,并将其应用于临时CD
FilterExpr:=“非”+FilterExpr;
ApplyCDSFilter(CD、FilterExpr);
//现在,我们可以遍历ClientDataSet1并测试文件名是否存在
//在临时CD中。如果是,则表示该文件名属于另一个文件名
//收藏也一样。
尝试
ClientDataSet1.DisableControls;
ClientDataSet1.First;
虽然不是ClientDataSet1.Eof,但要开始
AFi