Программа dupeGuru2.9.0 также позволяет пользователю настраивать алгоритмы сравнения файлов для более точного поиска, например: типы сканирования, жескость фильтра, заданному слову, разброс похожих слов и другие. При поиске программа выводит список найденных файлов с указанием путей, размеров и процентом совпадения.
Основные возможности:
• Быстрый поиск дубликатов, в том числе среди файлов разных форматов;
• настройка программы под конкретные нужды;
• Безопасность — ни один из нужных файлов не будет удален;
• Функция удаления пустых папок;
• Перемещение и копирование дубликатов, удаление плохих копий.
В этой версии исправлены некоторые ошибки:
• Значительно улучшена скорость и использование памяти при сканировании больших массивов.
• Добавлена поддержка drag& dropв панели папок. )
• Исправлена ошибка, приводившая dupeGuruк некорректному отчету при перемещении файлов во время сканирования.
• Прекращена поддержка MacOSX10.4 (Tiger)
DupeGuru— небольшая быстрая утилита для поиска дубликатов файлов на компьютере. Интерфейс программы предельно прост: нужно только выбрать область поиска дубликатов (Мои докуметы, все диски, отдельный каталог/типы файлов). Программа просканирует диск и выдаст результат в виде наглядной таблицы. dupeGuruможет просмотреть имена файлов и их содержание. Просмотр имени файла показывает четкий алгоритм соответствия, который может найти двойные имена файла даже когда они не точно похожи.
Алгоритм:
Производим рекурсивный поиск файлов, по каждому файлу создаем метку = "<имя_файла>:<размер>:<дата>", и сохраняем в бинарное дерево поиска, если такой метки нет, создаем новую, иначе найдены совпадения.
Реализация
Все операции необходимо выполнять в потоке.
type
TFileSearchThread = class(TThread)
private
_TickCountIntervalBeforeRefresh: integer;
locProgressMax: integer;
locProgressPosition: integer;
locSearchFilesDone: integer;
locCurrentFile: string;
procedure SetName;
procedure SetStatus;
procedure ThreadDone;
procedure FindFiles(
paramFolder,
paramMask: string;
const paramLevel: integer);
function GetFilesCount(
paramFolder: string;
const paramLevel :integer): integer;
procedure AddToTree(
var paramNode: TSearchTreeNode;
const paramPath: string;
const paramHash: string;
const FileSize: integer
);
protected
procedure Execute; override;
public
SearchFolder: string;
Mask: string;
FilesTree: TSearchTreeNode;
end;
{$IFDEF MSWINDOWS}
type
TThreadNameInfo = record
FType: LongWord; // must be 0x1000
FName: PChar; // pointer to name (in user address space)
FThreadID: LongWord; // thread ID (-1 indicates caller thread)
FFlags: LongWord; // reserved for future use, must be zero
end;
{$ENDIF}
{ TFileSearchThread }
procedure TFileSearchThread.SetName;
{$IFDEF MSWINDOWS}
var
ThreadNameInfo: TThreadNameInfo;
{$ENDIF}
begin
{$IFDEF MSWINDOWS}
ThreadNameInfo.FType := $1000;
ThreadNameInfo.FName := 'FileSearchThread';
ThreadNameInfo.FThreadID := $FFFFFFFF;
ThreadNameInfo.FFlags := 0;
try
RaiseException( $406D1388, 0, sizeof(ThreadNameInfo) div sizeof(LongWord), @ThreadNameInfo );
except
end;
{$ENDIF}
end;
procedure TFileSearchThread.Execute;
begin
SetName;
// Calculate less 2 sec
_TickCountIntervalBeforeRefresh := GetTickCount()+2000;
locProgressMax := 0;
locProgressPosition := 0;
GetFilesCount(SearchFolder, 0);
// Start search
_TickCountIntervalBeforeRefresh := 0;
findfiles(SearchFolder, Mask, 0);
// finish
synchronize(ThreadDone);
end;
Рекурсивный поиск файлов
procedure TFileSearchThread.FindFiles(
paramFolder,
paramMask: string;
const paramLevel: integer);
var
searchrec: tsearchrec;
findresult: integer;
hash: string;
begin
if self.Terminated then
exit;
if paramLevel <= uConst.cLevelCountForCalculateFilesCount then
Inc(locProgressPosition);
if locProgressPosition > locProgressMax then
locProgressMax := locProgressPosition;
// Update status
if GetTickCount() > _TickCountIntervalBeforeRefresh then
begin
//300 — update thread info interval in msec :
_TickCountIntervalBeforeRefresh := GetTickCount() + 300; //
locCurrentFile := paramFolder;
synchronize(SetStatus);
end;
paramFolder:=includetrailingbackslash(paramFolder);
findresult:=findfirst(paramFolder+paramMask, faanyfile, searchrec);
try
while findresult = 0 do
begin
if (searchrec.attr and fadirectory)<>0 then
begin
if (searchrec.name<>'.') and (searchrec.name<>'..') then
findfiles(paramFolder+searchrec.name, paramMask, paramLevel+1);
end
else
begin
hash := searchrec.name;
hash := hash + ':S'+IntToStr(searchrec.Size);
hash := hash + ':T'+IntToStr(searchrec.Time);
AddToTree(FilesTree,paramFolder+searchrec.name,hash,searchrec.Size);
end;
findresult:=findnext(searchrec);
end;
finally
findclose(searchrec);
end;
end;
Сохраняем метку файла в бинарное дерево поиска
procedure TFileSearchThread.AddToTree(
var paramNode: TSearchTreeNode;
const paramPath: string;
const paramHash: string;
const FileSize: integer
);
begin
if not Assigned(paramNode) then
begin
paramNode := TSearchTreeNode.Create(nil);
paramNode.Hash := paramHash;
paramNode.Files := paramPath+'|'+IntToStr(FileSize);
paramNode.FilesCount := 1;
end
else
begin
if paramHash > paramNode.Hash then
AddToTree(paramNode.HiNode,paramPath,paramHash,FileSize)
else
if paramHash < paramNode.Hash then
AddToTree(paramNode.LowNode,paramPath,paramHash,FileSize)
else
begin
paramNode.FilesCount := paramNode.FilesCount + 1;
paramNode.Files := paramNode.Files + #13#10+ paramPath+'|'+IntToStr(FileSize);
// count of entry duplicate files
if paramNode.FilesCount = 2 then
Inc(locSearchFilesDone);
end;
end;
end;
Предварительное определение общего количества файлов
Для удобства UI, а именно для отображения ProgressBar, нам необходимо приблизительно оценить общее количество файлов, для этого мы рекурсивно подсчитываем число каталогов с вложенностью 2-3(задаем параметром в конфигурационном фале).
functionTFileSearchThread.GetFilesCount(
paramFolder: string;
const paramLevel :integer): integer;
var
searchrec: tsearchrec;
findresult: integer;
hash: string;
begin
// Get folder count in hi 5 levels
if GetTickCount() > _TickCountIntervalBeforeRefresh then
exit;
if paramLevel > uConst.cLevelCountForCalculateFilesCount then
exit;
if self.Terminated then
exit;
Inc(locProgressMax);
paramFolder := includetrailingbackslash(paramFolder);
findresult:=findfirst(paramFolder+'*.*', faanyfile, searchrec);
try
while findresult = 0 do
begin
if (searchrec.attr and fadirectory)<>0 then
begin
if (searchrec.name<>'.') and (searchrec.name<>'..') then
GetFilesCount(paramFolder+searchrec.
end;
findresult:=findnext(searchrec);
end;
finally
findclose(searchrec);
end;
end;
|
||||||||||||
|
|
|