0

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

我有一个包含以下字段的 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;

我的问题是这可以提高效率还是有办法只使用 TClientDataset 方法来做同样的事情?

4

2 回答 2

1

iFilesInOtherCollectionsStringList.Sorted := True填完后再加。IndexOf然后将使用快速二进制搜索而不是极慢的一对一循环。可能这足以满足您的目的。

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

于 2014-10-28T16:46:52.537 回答
1

只是为了娱乐,我想我会尝试完全不使用字符串列表,而是使用 ClientDataSets 的几个功能,即过滤和在单个语句中将数据从一个 CDS 复制到另一个 CDS 的能力。它比使用字符串列表要短得多,因此可能更容易维护/修改/重构。

我没有将它与 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;
于 2014-10-28T17:27:28.150 回答