TClientDataSet和删除集合中的文件

时间:2014-10-28 15:16:48

标签: delphi delphi-xe7

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

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

2 个答案:

答案 0 :(得分:1)

填写后添加iFilesInOtherCollectionsStringList.Sorted := True即可。然后IndexOf将使用快速二进制搜索而不是极慢的逐个循环。可能这对你的目的来说足够了。

另一种选择是首先准备list-to-delete,然后启动一个将在后台执行删除的工作线程。这可能会有所帮助,因为通常文件操作比内存比较慢得多。您可以通过注释WindowsErase行来检查是否删除会降低您的流程速度。

答案 1 :(得分:1)

只是为了娱乐,我想我会尝试这样做而不使用Stringlists,而是使用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;