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