...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
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_82)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 ?
czy dodawać komentarze ? :)
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)?
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 ?
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ć???
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...)
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;