хеш таблица при работе с файлами

Jude
Дата: 17.05.2011 10:13:54
Тут умные книжки говорят, что хеш таблицы очень круто.
а вин апи - бытро.
задача по работе с файлами.

+
procedure TForm1.Button1Click(Sender: TObject);
var
hd:Cardinal;
fnm:string;
//rec:TSearchRec;
rec1:_win32_find_dataa;
 FindHandle : THandle;
 FindData : TWin32FindData;
 b:boolean;
 s:string;
begin
  //search;
//  fnm:='c:\*.*';
  FindHandle := FindFirstFile('C:\*.*', FindData);
  if FindHandle <> INVALID_HANDLE_VALUE then
  begin
     b := true;
     while b do
     begin
       s := FindData.cFileName;
       if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY)>0 then s:=s+' dir';
       // не показывать 
       if (s<>'..') and (s<>'.') then
         Listbox1.Items.Add(s);
       b := FindNextFile(FindHandle, FindData);
     end;
  end;
  FindClose(FindHandle);

end;


вот маленькую заготовку сделал - вычитываю в структуру TWin32FindData информацию о найденном файле (дальше будет рекурсия по директориям, не за нее сейчас речь)
потом надо обработать найденное. т.е. сверить имена, размеры, даты и совпадения юзеру предъявить.

вот с этого места про хеш таблицы.

так понял всю структуру хранить - не надо.
т.е.
 Tfrec = record
    parentdir: pointer;
    ftLastWriteTime: TFileTime;
    nFileSizeHigh: DWORD;
    nFileSizeLow: DWORD;
    cFileName: array[0..MAX_PATH - 1] of AnsiChar;
  end;
будет хватать.

дальше рекомендуют сделать CRC стоке. cFileName = строка. была мысль сделать ftLastWriteTime+ nFileSizeHigh +nFileSizeLow+cFileName = строкой и ее хешировать.
и потом по поступлении новых Tfrec смотреть вновь полученный хеш - и если там уже что-то есть = решать про коллизии и подвязывать новый эллемент или определять что это совпадение и сигналить за это юзеру.

если CRC16 то 65536 + коллизии = массив на мин 128kb (вроде не смертельно)

потом Tfrec все равно придется где-то в памяти хранить (иначе как понять колизия ли) + если таки будет 2 элеменка на 1 ключ,
это доп цикл с пробежать по всем элементам структуры + сравнение cFileName = медленно. а если еще при этом пробегать все позиции ветки с одним ключем...
допустим даже если cFileName = cFileName делать через исключающее или (чтоб бодрее) - есть неясность с длинной записи.

тут вот пытался поискать, из того, что есть готовое по заявленной проблемме.
есть TBucketList (но что-то его ругают. с хелпом там немного не понятно. )
есть чувство, что в винде что-то подобное уже реализовано, отполировано, и просто я не знаю как это достать из апи.

Внимание вопрос:
1) есть ли в винапи что-то наподобии хеш -таблицы, которой можно безбоязненно воспользоваться для описанного выше?
2) просьба подсказать по вин апи, что глянуть по теме.
3) есть ли БЫСТРЫЙ способ не создавать мегабайты в памяти с информацией про файловую структуру - а работать с указателями (подозреваю, что должно быть)

Спасибо за ответы и подсказки.

п.с. если делать элемент хеш таблицы так : ключ - указатель на структуру; указатель на след элемент; и ориентироваться на nil - как на конец ветви = нормальный подход?
Maxim Rusov
Дата: 17.05.2011 10:22:12
Сделай сначала простой сортированный список, может результат тебя и так устроит. Выигрыш хешей заметен только на очень больших объемах, на миллионы элементов.
fd00ch
Дата: 17.05.2011 10:41:20
Jude
есть чувство, что в винде что-то подобное уже реализовано, отполировано, и просто я не знаю как это достать из апи.
Святая вера в API... Обычные делфикодеры свято верят в силу компонентов, а ты - в API, что-то с тобой не так :-)

Jude
задача по работе с файлами.
В чем задача то заключается (я так и не понял из рассказа)? Пока что похоже на очередную сомнительную тему об оптимизации...
Jude
Дата: 17.05.2011 11:34:05
fd00ch
Jude
есть чувство, что в винде что-то подобное уже реализовано, отполировано, и просто я не знаю как это достать из апи.
Святая вера в API... Обычные делфикодеры свято верят в силу компонентов, а ты - в API, что-то с тобой не так :-)

Jude
задача по работе с файлами.
В чем задача то заключается (я так и не понял из рассказа)? Пока что похоже на очередную сомнительную тему об оптимизации...


хочу попробовать нарисовать программу, которая будет ловить файлы, которые совпадают по определенному признаку.
надо мне это, для того, чтоб разобраться с 1) мультипоточностью, 2) апи 3) оптимизацией по времени. 4) поставлена мне такая задача.

вера в апи - скорее здравый смысл: в винде есть уже поиск по файлам. хоть и корявый - но есть. так что и нечто подобное должно быть. возможно оно сделано так, что пользоваться им никто, кроме винды, не может, и легче сделать свое - тут поверить могу. но быть оно должно.

п.с. пытаюсь получить levelup в винапи. нужна практика. вот и "набиваю руку". иначе не честно будет писать в резюме "знаю винапи" без "и много других страшных слов" ;) .
fd00ch
Дата: 17.05.2011 12:05:42
Jude
пытаюсь получить levelup в винапи
Имхо, для начала тебе надо получить levelup в формулировке задания и грамотном выборе пути его решения.

Приплетать к какой-то мифической задаче по "ловли файлов" (sic!) многопоточность, апи (это что вообще - умение вызвать функцию? дык таких вызовов в каждой программе по тысяче) и оптимизацию по времени...
Ega
Дата: 17.05.2011 12:13:33
купи Total Commander. Цена вопроса равна цене оплаты половины рабочего дня delphi-программиста средней квалификации в Москве.
_MDA_
Дата: 17.05.2011 12:49:33
Тебе хэш даст ответ на вопрос - есть ли такой файл в списке, к примеру. А вот ответ на вопрос - есть ли такой файл, но с другим атрибутом - уже нет.
Jude
Дата: 18.05.2011 00:30:43
как-то так:

+
unit Unit1;

interface

uses
  Windows, Messages, StdCtrls, Classes, Controls, ComCtrls, Variants,  Graphics,  Forms,
  Dialogs,  CheckLst,Contnrs, sysutils ;

type
  pfrec = ^Tfrec;
  Tfrec = record
    pdir:pointer;
    ftLastWriteTime: TFileTime;
    nFileSizeHigh: DWORD;
    nFileSizeLow: DWORD;
    cFileName: array[0..MAX_PATH - 1] of AnsiChar;
  end;
  pfdubl = ^tfdubl;
  Tfdubl = record
    a:tfrec;
    booble:pfdubl;
  end;
  Tcollise=class
    flastdubl:pfrec;
    flist: array of pfrec;
  public
    function add(const newitem:pfrec):boolean;
    function compare(const a,b:pfrec):boolean;
  end;
  pdir = ^tdir;
  Tdir = record
    dirnam: array[0..MAX_PATH - 1] of AnsiChar;
    nextdir:pdir;

   end;
  Tfdlist=class
  fpointers:array[0..2208] of pfdubl;
  flist: array of Tfdubl;

  public
    function add(const a : pfrec; const id:word):boolean;//tru= more then one doobl;
  end;


  Tdirlist=class
    Fdir :array [0..2208] of Tcollise;
    frezfl:Tfdlist;
    fcollist: array of Tfrec;
  public
    function add(const a:Tfrec): boolean;
    function hesh(const a:pfrec):word;

  end;






  TForm1 = class(TForm)
    ListView1: TListView;
    ListView2: TListView;
    ComboBox1: TComboBox;
    Button1: TButton;
    ListBox1: TListBox;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);


    { Private declarations }

  public
    { Public declarations }
    answer: array [0..255]of byte;
    dirlist:Tdirlist;
  end;
//TBucketList



var
  Form1: TForm1;




implementation

{$R *.dfm}
function AlgoritmForDword(a:Dword):Longint;
var i:dword;
begin
  i:= not dword(a);
  i:=((i and $55555555) + ((i shr 1 ) and $55555555) );
  i:=((i and $33333333) + ((i shr 2 ) and $33333333) );
  i:=((i and $0F0F0F0F) + ((i shr 4 ) and $0F0F0F0F) );
  i:=((i and $00FF00FF) + ((i shr 8 ) and $00FF00FF) );
  i:=((i and $FFFF) + ((i shr 16 ) and $FFFF) );
  result:=i;
end;
function AlgoritmForAnsiChar(a:Ansichar):byte;
var i:dword;
begin
  i:= not byte(a); //becose we calk 0, not 1 invert
  i:=((i and $55) + ((i shr 1 ) and $55) ); //sum every first and second bit. this step we have 4 results in each byte.
  i:=((i and $33) + ((i shr 2 ) and $33) ); // sum erery first and second result. this step we have 2 results each byte.
  i:=((i and $0F) + ((i shr 4 ) and $0F) ); // sum first and sedond part of byte. this step we have 1 byte- 1 result (0..8 - quality of 0 bits).
  result:=i;
end;

    function Tcollise.compare(const a,b:pfrec):boolean;
    begin
      result := true;
      result := (a^.nFileSizeHigh = b^.nFileSizeHigh);
      if not result then exit;
      result := (a^.nFileSizeLow = b^.nFileSizeLow);
      if not result then exit;
      result := (a^.ftLastWriteTime.dwLowDateTime = b^.ftLastWriteTime.dwLowDateTime);
      if not result then exit;
      result := (a^.ftLastWriteTime.dwHighDateTime = b^.ftLastWriteTime.dwHighDateTime);
      if not result then exit;
      result := (pos(a^.cFileName ,b^.cFileName)=1);
    end;


    function Tcollise.add(const newitem:pfrec):boolean;
    var i:integer;
    begin
      result := true;
      if length(flist) = 0
      then
      begin
        setlength( flist,1);
        flist[0] := newitem;
      end else
      begin
      for i:= 0 to  length(flist)-1 do
        if compare( flist[i],newitem) then
        begin
          result:=false;
          flastdubl := @flist[i];
          exit;
        end;
      setlength( flist,i+2);
      flist[i+1] := newitem;
      end;
    end;

    function Tdirlist.hesh(const A:pfrec):word;
      var p:pchar;
      m:dword;
    begin
      p:=pointer(a);
      inc(p,4);
      result:=0;
      m:=16+strlen(a^.cFileName);
      while m>=4 do
      begin
        inc(result,AlgoritmForDword(pdword(p)^));
        inc(p,4);
        dec(m,4);
      end;

      while m>0 do
      begin
        inc(result,AlgoritmForAnsiChar(p^));
        inc(p);
        dec(m);
      end;

    end;
    function Tdirlist.add(const a:Tfrec): boolean;
    var b:word; rp:pfrec;
    begin
      
      setlength(fcollist,length(fcollist)+1);
      fcollist[length(fcollist)-1]:=a;
      rp:=@fcollist[length(fcollist)-1];
      b:= hesh(rp);
      if Fdir[b]= nil then
      Fdir[b] := tcollise.Create;
      result := Fdir[b].add(rp);
      if not result then
      begin
        if not frezfl.add(rp,b) then frezfl.add(Fdir[b].flastdubl,b);

      end;
    end;

    function Tfdlist.add(const a : pfrec; const id:word):boolean;
    var p:pfdubl;
    begin
      result:=false;
      setlength(flist,length(flist)+1);
      flist[length(flist)-1].a:=a^;
      flist[length(flist)-1].booble :=nil;

      if fpointers[id]=nil then
      begin
        fpointers[id]:=@flist[length(flist)-1];
      end else
      begin
        result:=true; // tru= more then one doobl;
        p:=fpointers[id];
        while p.booble <> nil do p:=p.booble;
        p.booble:= @flist[length(flist)-1];
      end;
    end;

procedure TForm1.Button1Click(Sender: TObject);
var
 j:integer;
 p: pfdubl;
 procedure search(a:string);
 var
 FindHandle : THandle;
 FindData : TWin32FindData;
 b:boolean;
 s:string;
 tmprec:Tfrec;
 i:integer;
 begin
  FindHandle := FindFirstFile(PCHAR(a), FindData);
  if FindHandle <> INVALID_HANDLE_VALUE then
  begin
     b := true;
     while b do
     begin
       s := FindData.cFileName;


       // âñÿêèå òî÷êè è äâîåòî÷èÿ íàì íå íóæíû
       if (s<>'..') and (s<>'.') then

       if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY)>0
       then
          search(copy(a,1,pos('*.*',a)-1) + s + '\' + '*.*')
       else
       begin


         tmprec.ftLastWriteTime.dwLowDateTime:=FindData.ftLastWriteTime.dwLowDateTime;
         tmprec.ftLastWriteTime.dwHighDateTime:=FindData.ftLastWriteTime.dwHighDateTime;
         tmprec.nFileSizeHigh:=FindData.nFileSizeHigh;
         tmprec.nFileSizeLow:=FindData.nFileSizeLow;
         for i:= 0 to high(tmprec.cFileName) do
         tmprec.cFileName[i] := FindData.cFileName[i];
         dirlist.add(tmprec);
       end;
       b := FindNextFile(FindHandle, FindData);
     end;
  end;
  windows.FindClose(FindHandle);
 end;

begin
  button1.Enabled:=false;
  try
    search('c:\test\*.*');
    for j:=0 to high(dirlist.frezfl.fpointers) do
    begin
      if dirlist.frezfl.fpointers[j]=nil then continue ;

      p:=dirlist.frezfl.fpointers[j];
      while p<>nil do
      begin
        ListBox1.Items.Add(p.a.cFileName);
        p:=p.booble;
      end
      
    end;

  finally
    button1.Enabled:=true;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var i:integer;
begin
   //prepeare bitmask
  for i := 0 to 255 do
  begin
    answer[i] := AlgoritmForAnsiChar(char(i));
  end;
  dirlist := Tdirlist.create;
  dirlist.frezfl := Tfdlist.Create;
end;
Jude
Дата: 18.05.2011 00:36:36
Jude
как-то так:


пока сырое, коряво работает и с ошибками. но общая идея вроде бы обозначена.

принимаю комментрии, советы.

и подсказки как сделать, чтобы быстрее и лучше работало.

прошу помнить, что это второе мое самостоятельно написанное приложение, с использованием винапи, и первое - с хеш-таблицами.

пока все в процессе - а в 10утра - на работу ))).
так что кто полуношничает - wellcome.

п.с. главное не заснуть на backspace ))))
fd00ch
Дата: 18.05.2011 00:50:53
Jude, ты так и не описал задачу :)

Первое что бросилось в глаза - нафига ты хранишь имя файла в своей записи внутри array of Char? как итог этого - потребление целой кучи памяти впустую + медленное копирование из TFindData внутрь Tfrec + медленное вычисление длины + регистрозависимое сравнение

Остальное даже не смотрел, т.к. многабукф, нет задания и особого желания разбирать это всё :)