lista plików -> struktura drzewka w pamięci

0

...czyli innymi słowy gdzie mogę (?) znaleźć kod lub biblioteki, które by z listy plików z pełną ścieżką dostępu utworzyły w pamięci (pamięcioszczędnie) 'drzewko' struktury którą budują ?
=>TIA

0

a więc pracuję nad tym. algorytm praktycznie jest gotowy tylko coś się dziwnie zachowuje. tzn gdzieś jest błąd. w 'test-preview code4' (pod koniec) otrzymuję acces violation, niestety jednak nie zawsze, raz tak raz nie. ślęczałem nad tym parę godzin śledziłem linijka po linijce ale jak nie chciało działać tak nie działa.
algorytm jest w ogóle nie zoptymalizowany poczynając od quicksorta (TStringList.CustomSort) który de facto jest zbędny, poprzez wolne AnsiContainsText, zbałaganione zmienne iteracyjne a kończąc na niepotrzebnych nil-ach które dodałem dla pewności, paru setlength(tralala,0) i innych rzeczach - piszę to (na wszelki wypadek) bo nie jestem zainteresowany komentarzami że mogłem to zrobić tak albo inaczej - wiem (ale najpierw to musi działać żeby było co optymalizować, przynajmniej ja nie umiem inaczej).
wątpię żeby się komuś chciało, ale ponieważ post nie miał odezwu a to pewnie znaczy że nikt takiego kodu nie ma (który by coś takiego robił), to może jednak ktoś mi pomoże i wtedy będzie :).

jak algorytm działa:
(jak ktoś ma inny pomysł na mechanizm niech się podzieli pomysłem ! - przy czym od razu piszę że nie interesuje mnie drzewko do którego mam dostęp tylko od góry)

1)posortowanie według zagłębienia ('')
D:\Plik_1
D:\Plik_2
D:\Folder_1\Plik_3
D:\Folder_2\Plik_4
D:\Folder_2\Plik_5
D:\Folder_2\Folder_3\Plik_6
D:\Folder_2\Folder_4\Folder_5\Plik_7
D:\Folder_2\Folder_4\Folder_6\Folder_7\Plik_8

2)podzielenie według zagłębienia

D:\Plik_1
D:\Plik_2
D:\Folder_1
D:\Folder_2\

D:\Folder_1\Plik_3
D:\Folder_2\Plik_4
D:\Folder_2\Plik_5
D:\Folder_2\Folder_3
D:\Folder_2\Folder_4\

D:\Folder_2\Folder_3\Plik_6
D:\Folder_2\Folder_4\Folder_5
D:\Folder_2\Folder_4\Folder_6\

D:\Folder_2\Folder_4\Folder_5\Plik_7
D:\Folder_2\Folder_4\Folder_6\Folder_7\

D:\Folder_2\Folder_4\Folder_6\Folder_7\Plik_8

3)po określeniu maksymalnego zagłębienia (poprzez liczbę'')
-tablica 1 wymiar
[0]
[0]
[0]
[0]
[0]

4)po określeniu ilości folderów i plików na poziom
-tablica 2 wymiar
[4]
[5]
[3]
[2]
[1]

czyli:

[folder][folder][plik][plik]
[folder][folder][plik][plik][plik]
[folder][folder][plik]
[folder][plik]
[plik]

5)następnie określenie zależności przez wskaźniki (bazując na podobieństwie ścieżek itemów) :
-dzieci jako tablica 3 wymiar

                       [folder]        [        folder       ]     [plik]     [plik]
                          |              |    |       |     |
                       [plik]       [folder][folder][plik][plik]
                                     |    |       |
                                [folder][folder][plik]
                                  |         |
                              [folder]    [plik]
                                  | 
                         [plik]

żeby to testować potrzebne są 2 ListBox'y (u mnie jeden AHCustomListBox)
(czemu nie jeden nie wiem tak wyszło), Buttom i żródłowa lista, u mnie zaczerpnięta z AHCustomListBox (SortList.Assign(TAHCustomListBox(FileList).Items);)

aha, błąd prawdopodobnie powoduje ta część (1):

for i:=high(LevelsFilesArr) downto 0 do
    for j:=0 to (LevelsFilesArr[i].Count-1) do
        for k:=0 to (LevelsFoldersCountArr[i]) do
            if ExtractFilePath(LevelsFilesArr[i].Strings[j])=FinalTree[i,k].ItemName then
               begin
               SetLength(FinalTree[i+1],high(FinalTree[i+1])+2);

               FinalTree[i+1,high(FinalTree[i+1])].ItemName:=LevelsFilesArr[i].Strings[j];
               FinalTree[i+1,high(FinalTree[i+1])].ParentItem:=@FinalTree[i,k];

               setlength(FinalTree[i,k].SubItems,high(FinalTree[i,k].SubItems)+2);
               FinalTree[i,k].SubItems[high(FinalTree[i,k].SubItems)]:=@FinalTree[i+1,high(FinalTree[i+1])];
               break;
               end;

bez niej dostęp do pierwszego elementu FinalTree[0,0].SubItems jest możliwy zawsze:

for i:=0 to high(FinalTree[0,0].SubItems) do
    ListBox1.Items.Add(FinalTree[0,0].SubItems[i]^.ItemName);

po włączeniu (1) pojawiają sięAV (ok 50%).

mam nadzieję że pare angielskich komentarzy to nie problem...
a to kod:...

  PTreeItem = ^TTreeItem;

  TTreeItem = record
     ItemName : widestring; //ścieżka do folderu, nazwa pliku
     IsFolder : boolean; //folder lub nie
   ParentItem : PTreeItem; //właściciel folderu
     SubItems : array of PTreeItem; //tablica wszystkich pod-elementów, plików i
     end;

procedure TMain.Button18Click(Sender: TObject);
var         i,j,k,l,m : integer;
     LevelsFoldersArr : array of TTntStringList;
       LevelsFilesArr : array of TTntStringList;
             SortList : TTntStringList;
            FinalTree : array of array of TTreeItem;
LevelsFoldersCountArr : array of word;
begin
SortList:=TTntStringList.Create;
SortList.Assign(TAHCustomListBox(FileList).Items);
SortList.CustomSort(BackslashWideStringListCompareStrings);

i:=CountCharPos('\',SortList[0]);
l:=i;
k:=0;
m:=0;
repeat
SetLength(LevelsFoldersArr, high(LevelsFoldersArr)+2);
SetLength(LevelsFilesArr,  high(LevelsFilesArr)+2);
LevelsFoldersArr[i-l-m]:=TTntStringList.Create;
LevelsFoldersArr[i-l-m].Sorted:=true;
LevelsFilesArr[i-l-m]:=TTntStringList.Create;
repeat
LevelsFilesArr[i-l-m].Add(SortList[k]);
LevelsFoldersArr[i-l-m].Add(ExtractFilePath(SortList[k]));
inc(k);
if k<=(SortList.Count-1) then
   j:= CountCharPos('\',SortList[k])
else break;
until i<>j;
inc(m,j-i-1);
i:=j;
until k>(SortList.Count-1);

SortList.Free;

SetLength(FinalTree,high(LevelsFilesArr)+1);
for i:=0 to high(FinalTree) do
    begin
    SetLength(FinalTree[i],LevelsFoldersArr[i].Count);
    for j:=0 to high(FinalTree[i]) do
        begin
        FinalTree[i,j].ParentItem:=nil;
        FinalTree[i,j].SubItems:=nil;
        FinalTree[i,j].ItemName:=LevelsFoldersArr[i].Strings[j];
        FinalTree[i,j].IsFolder:=true;
        end;
    end;

i:=high(FinalTree);
while i>0 do
    begin
    j:=0;
    while j<=high(FinalTree[i]) do//folder on levels
        begin
        if (CountCharPos('\',FinalTree[i,j].ItemName)-CountCharPos('\',FinalTree[i-1,0].ItemName))=1 then
           begin
           for k:=0 to high(FinalTree[i-1]) do//search for parent folder on upper level
               if AnsiContainsText(FinalTree[i,j].ItemName,FinalTree[i-1,k].ItemName) then
                  begin
                  FinalTree[i,j].ParentItem:=@FinalTree[i-1,k];//setting parent for the folder
                  setlength(FinalTree[i-1,k].SubItems,high(FinalTree[i-1,k].SubItems)+2);
                  FinalTree[i-1,k].SubItems[high(FinalTree[i-1,k].SubItems)]:=@FinalTree[i,j];//setting as a child for the parent
                  break;
                  end;
           if FinalTree[i,j].ParentItem=nil then//if parent not found, parent is a folder with only folders inside, add new folder on upper level
              begin
              SetLength(FinalTree[i-1],k+1);
              inc(k);
              FinalTree[i-1,k-1].ItemName:=ExtractFilePath(ExtractFileDir(FinalTree[i,j].ItemName));
              FinalTree[i-1,k-1].IsFolder:=true;
              FinalTree[i-1,k-1].ParentItem:=nil;
              FinalTree[i-1,k-1].SubItems:=nil;
              FinalTree[i,j].ParentItem:=@FinalTree[i-1,k];//setting parent for the folder
              setlength(FinalTree[i-1,k-1].SubItems,high(FinalTree[i-1,k-1].SubItems)+2);
              FinalTree[i-1,k-1].SubItems[high(FinalTree[i-1,k-1].SubItems)]:=@FinalTree[i,j];//setting as a child for the parent
              end;
           end
        else
           begin
           SetLength(LevelsFilesArr,high(LevelsFilesArr)+2);
           LevelsFilesArr[high(LevelsFilesArr)]:=TTntStringList.Create;
           for k:=high(LevelsFilesArr) downto (i+1) do
               begin
               LevelsFilesArr[k].Clear;
               LevelsFilesArr[k].AddStrings(LevelsFilesArr[k-1]);
               end;
           LevelsFilesArr[i].Clear;

           SetLength(FinalTree,high(FinalTree)+2);
           for k:=high(FinalTree) downto (i+1) do
               FinalTree[k]:=FinalTree[k-1];
           setlength(FinalTree[i],0);
           setlength(FinalTree[i],1);
           FinalTree[i,0].ItemName:=ExtractFilePath(ExtractFileDir(FinalTree[k+1,j].ItemName));
           FinalTree[i,0].IsFolder:=true;
           FinalTree[i,0].ParentItem:=nil;
           FinalTree[i,0].SubItems:=nil;           
           FinalTree[i+1,j].ParentItem:=@FinalTree[i,0];//setting parent for the folder
           setlength(FinalTree[i,0].SubItems,high(FinalTree[i,0].SubItems)+2);
           FinalTree[i,0].SubItems[high(FinalTree[i,0].SubItems)]:=@FinalTree[i+1,j];//setting as a child for the parent
           inc(i);
           end;
        inc(j);
        end;
    dec(i);
    end;


//test-preview code1
TAHCustomListBox(FileList).Items.Clear;
for i:=0 to high(LevelsFoldersArr) do
    begin
    TAHCustomListBox(FileList).Items.AddStrings(LevelsFoldersArr[i]);
    TAHCustomListBox(FileList).Items.Add(' ');
    end;
TAHCustomListBox(FileList).Items.Add('----------');
//\test-preview code1


//test-preview code2
for i:=0 to high(LevelsFilesArr) do
    begin
    TAHCustomListBox(FileList).Items.AddStrings(LevelsFilesArr[i]);
    TAHCustomListBox(FileList).Items.Add(' ');
    end;
TAHCustomListBox(FileList).Items.Add('----------');
//\test-preview code2

//test-preview code3
for i:=0 to high(FinalTree) do
    begin
    for j:=0 to high(FinalTree[i]) do
        TAHCustomListBox(FileList).Items.Add(FinalTree[i,j].ItemName);
    TAHCustomListBox(FileList).Items.Add(' ');
    end;
TAHCustomListBox(FileList).Items.Add('----------');
//\test-preview code3

SetLength(LevelsFoldersCountArr,high(FinalTree)+1);
SetLength(FinalTree,high(FinalTree)+2);
for i:=0 to high(LevelsFoldersCountArr) do
    LevelsFoldersCountArr[i]:=high(FinalTree[i]);
for i:=0 to High(LevelsFoldersArr) do
    LevelsFoldersArr[i].Free;
SetLength(LevelsFoldersArr,0);
//prawdopodobnie ten blok powoduje AV
for i:=high(LevelsFilesArr) downto 0 do
    for j:=0 to (LevelsFilesArr[i].Count-1) do
        for k:=0 to (LevelsFoldersCountArr[i]) do
            if ExtractFilePath(LevelsFilesArr[i].Strings[j])=FinalTree[i,k].ItemName then
               begin
               SetLength(FinalTree[i+1],high(FinalTree[i+1])+2);

               FinalTree[i+1,high(FinalTree[i+1])].ItemName:=LevelsFilesArr[i].Strings[j];
               FinalTree[i+1,high(FinalTree[i+1])].ParentItem:=@FinalTree[i,k];

               setlength(FinalTree[i,k].SubItems,high(FinalTree[i,k].SubItems)+2);
               FinalTree[i,k].SubItems[high(FinalTree[i,k].SubItems)]:=@FinalTree[i+1,high(FinalTree[i+1])];
               break;
               end; 

//test-preview code4
for i:=0 to high(FinalTree[0,0].SubItems) do 
    ListBox1.Items.Add(FinalTree[0,0].SubItems[i]^.ItemName); //tutaj zonk !!!
//\test-preview code4
for i:=0 to High(LevelsFilesArr) do
    LevelsFilesArr[i].Free;
SetLength(LevelsFilesArr,0);
SetLength(LevelsFoldersCountArr,0);
for i:=0 to high(FinalTree) do
    begin
    for j:=0 to High(FinalTree[i]) do
        SetLength(FinalTree[i,j].SubItems,0);
    SetLength(FinalTree[i],0);    
    end;    
SetLength(FinalTree,0);
end;

[EDIT]
potem dodam więcej komentarzy, chyba że nikt nawet nie jest zainteresowany ?

0

czy dodawać komentarze ? :)

0

1)posortowanie według zagłębienia ('')
...
2)podzielenie według zagłębienia
...

A po co te sortowanie??? Nie wystarczy zwykłe dzewo i analiza kolejnych ścieżek (niekoniecznie posortowanych)?

0
0x666 napisał(a)

1)posortowanie według zagłębienia ('')
...
2)podzielenie według zagłębienia
...

A po co te sortowanie???

tylko dla ułatwienia

Nie wystarczy zwykłe dzewo i analiza kolejnych ścieżek (niekoniecznie posortowanych)?

co to jest zwykłe drzewo ? takie coś z dostępem tylko od góry ?
|
/|
/||
jeśli tak, to analiza kolejnych ścieżek byłaby wydaje mi się bardziej czasochłonna bo szukanie rodzica danego pliku wymagałoby chodzenia po drzewku tam i z powrotem/na górę i na dół (w mojej metodzie nie)
[EDIT]
ort
[EDIT2]
błąd (AV) występuje w kernel32.dll, czy to coś wyjaśnia ?

0

co to jest zwykłe drzewo ? takie coś z dostępem tylko od góry ?

Zwykłe drzewo to takie jakie masz np. w tree view ;) Mówisz, że chcesz odtworzyć drzewo katalogów na podstawie kilku ścieżek więc najprościej będzie zrobić to na takim drzewie.

[...] wymagałoby chodzenia po drzewku tam i z powrotem/na górę i na dół (w mojej metodzie nie)

No w dół to oczywiste... ale w górę? po co? Na samą górę (root node) wracasz tylko gdy analizujesz nową ścieżkę. Nawet jeżeli masz na myśli odczytanie ścieżki z takiego drzewa, no to zastanów się, z jaką maskymalną głębokością drzewa (katalogów) spotkałeś się podczas użytkowania systemu? 10, 20??? ;P Dodam, że kiedyś coś podobnego robiłem w C++ i wszystko elegancko chodziło.

PS. Do czego to ma służyć???

0
0x666 napisał(a)

No w dół to oczywiste... ale w górę? po co? Na samą górę (root node) wracasz tylko gdy analizujesz nową ścieżkę.

właściwie to masz racje. nie wiem po co do góry :|
spróbuję to też tak zrobić.

0x666 napisał(a)

PS. Do czego to ma służyć???

logowanie rezultatów pracy nad plikiem :), na różne sposoby (dla każdego osobno, dla zawartości bieżącej folderu, dla całego drzewa pod folderem itp...)

0

dzięki za sugestie 0x666 bo bez nich bym bewnie jeszcze długo błądził :), oto kod jakby ktoś szukał, unikodowy:
(pozdro !)
[EDIT] kod współpracuje tylko z plikami na jednym dysku podanie mu innych kończy się zapętleniem, no ale zmiana tego to nie problem ;)

PTreeItem = ^TTreeItem;

  TTreeItem = record
     ItemName : widestring;
     IsFolder : boolean;
     SubItems : array of PTreeItem;
     end;

function CountCharPos(const subtext: widestring; Text: widestring): Integer;
begin
  if (Length(subtext) = 0) or (Length(Text) = 0) or (WidePos(subtext, Text) = 0) then
    Result := 0
  else
    Result := (Length(Text) - Length(Tnt_WideStringReplace(Text, subtext, '', [rfReplaceAll]))) div Length(subtext);
end;




procedure MakeParent(var Child : PTreeItem);
var
NewItem : PtreeItem;
begin
NewItem:=AllocMem(sizeof(TTreeItem));
NewItem.ItemName:=WideExtractFileDir(Child.ItemName);
NewItem.IsFolder:=true;
setlength(NewItem.SubItems,1);
NewItem.SubItems[0]:=Child;
Child:=NewItem;
end;


procedure LinkFolders(var Folder1, Folder2 : PTreeItem);
var
CurrentItem : PTreeItem;
i,j : longword;
begin
if WideExtractFileDir(Folder1.ItemName)<>WideExtractFileDir(Folder2.ItemName) then
   begin
   i:=CountCharPos('\',Folder1.ItemName)-CountCharPos('\',Folder2.ItemName);
   if i<0 then
   for j:=0 to abs(i)-1 do
       MakeParent(Folder2)
   else
      for j:=0 to abs(i)-1 do
          MakeParent(Folder1);
   while WideExtractFileDir(Folder1.ItemName)<>WideExtractFileDir(Folder2.ItemName) do
      begin
      MakeParent(Folder1);
      MakeParent(Folder2);
      end;
   end;
MakeParent(Folder1);
SetLength(Folder1.SubItems,2);
Folder1.SubItems[1]:=Folder2;
end;



procedure BuildTree(const FileList : TTntStrings; out Tree : PTreeItem);
var
i,j : longword;
NewItem, CurrentItem : PTreeItem;
begin
for i:=0 to FileList.Count-1 do
    begin
    NewItem:=AllocMem(sizeof(TTreeItem));
    NewItem.ItemName:=FileList[i];
    NewItem.IsFolder:=false;
    if Tree=nil then
       begin
       MakeParent(NewItem);
       Tree:=NewItem;
       end
    else
       begin
       if WidePos(Tree.ItemName,NewItem.ItemName)<>0 then
          begin
          if (Tree.ItemName+'\'=WideExtractFileDir(NewItem.ItemName)) then
             begin
             setlength(Tree.SubItems,high(Tree.SubItems)+2);
             Tree.SubItems[high(Tree.SubItems)]:=NewItem;
             end
          else
             begin
             CurrentItem:=Tree;
             j:=0;
             repeat
             while (j<=high(CurrentItem.SubItems))and(not CurrentItem.SubItems[j].IsFolder) do inc(j);
             if (j<=high(CurrentItem.SubItems)) then
                begin
                if WidePos(CurrentItem.SubItems[j].ItemName+'\',WideExtractFilePath(NewItem.ItemName))<>0 then
                   begin
                   CurrentItem:=CurrentItem.SubItems[j];
                   j:=0;
                   end
                else
                   inc(j);   
                end
             else
                break;   
             until (j>high(CurrentItem.SubItems))  or (WideExtractFileDir(NewItem.ItemName)=CurrentItem.ItemName);
             if (WideExtractFileDir(NewItem.ItemName)<>CurrentItem.ItemName) then
                begin
                repeat
                MakeParent(NewItem);
                until ExtractFileDir(NewItem.ItemName)=CurrentItem.ItemName;
                setlength(CurrentItem.SubItems,high(CurrentItem.SubItems)+2);
                CurrentItem.SubItems[high(CurrentItem.SubItems)]:=NewItem;
                end
             else
                begin
                setlength(CurrentItem.SubItems,high(CurrentItem.SubItems)+2);
                CurrentItem.SubItems[high(CurrentItem.SubItems)]:=NewItem;
                end
             end
          end
       else
          begin
          MakeParent(NewItem);
          LinkFolders(NewItem, Tree);
          Tree:=NewItem;
          end;
       end;
    end;
end;

1 użytkowników online, w tym zalogowanych: 0, gości: 1