Discussion:
Teil-Pfad
(zu alt für eine Antwort)
Lothar Planitzer
2015-06-06 10:39:33 UTC
Permalink
Hallo,

folgendes Problem:

gegeben sei eine Liste von Dateinamen z.B.

G:\Audio\Itunes Mediatheken\iMainLib\iTunes Media\Music\Johnny Burnette
Trio\Honey Hush\17 All By Myself.m4a
G:\Audio\Itunes Mediatheken\iMainLib\iTunes Media\Music\The Everly Brothers\24
Original Classics\06 All I Have To Do Is Dream.m4a
G:\Audio\Itunes Mediatheken\iMainLib\iTunes Media\Music\Carl Perkins\All Shook
Up\05 All Shook Up.m4a
G:\Audio\Itunes Mediatheken\iMainLib\iTunes Media\Music\Various Artists\Real Raw
Rockabilly\1-17 All The Time.m4a
G:\Audio\Itunes Mediatheken\iMainLib\iTunes Media\Music\Chuck Berry\Chuck Berry
Is On Top\01 Almost Grown.m4a
G:\Audio\Itunes Mediatheken\iMainLib\iTunes Media\Music\Sun\Classic Carl Perkins
[Disc 5]\5-24 Anyway The Wind Blows [Single-V.m4a
G:\Audio\Itunes Mediatheken\iMainLib\iTunes Media\Music\Brenda Lee\Brenda Lee -
All Time Greatest Hits [Dis\10 As Usual.m4a

Aus dieser Liste, die bis zu 10000 Elemente enthalten kann, soll der gemeinsame
Teilpfad, in diesem Fall
G:\Audio\Itunes Mediatheken\iMainLib\iTunes Media\Music\
ermittelt werden.

Hat jemand eine Idee?

Gruß Lothar
Jakob Achterndiek
2015-06-06 12:54:53 UTC
Permalink
Am 06.06.2015, 12:39 Uhr, schrieb Lothar Planitzer
Post by Lothar Planitzer
Hallo,
gegeben sei eine Liste von Dateinamen z.B.
G:\Audio\Itunes Mediatheken\iMainLib\iTunes Media\Music\
Johnny Burnette Trio\Honey Hush\17 All By Myself.m4a
[ usw. ]
Aus dieser Liste, die bis zu 10000 Elemente enthalten kann,
soll der gemeinsame Teilpfad, in diesem Fall
G:\Audio\Itunes Mediatheken\iMainLib\iTunes Media\Music\
ermittelt werden.
Hat jemand eine Idee?
Nur mal eben aus dem Handgelenk:
Ein Formular mit Listbox1, Edit1 und Button 1.
Die Dateien stehen in der Listbox1.
Dann

procedure TForm1.Button1Click(Sender: TObject);
var
i, p: integer;
S, T: string;
begin
S := Listbox1.Items[0];
p := Length(S);
repeat
dec(p)
until S[p] = '\';
T := copy(S,1,p);
for i:=0 to Listbox1.Items.Count-1 do begin
S := copy(Listbox1.Items[i],1,Length(T));
if S = T then Edit1.Text := T
else begin
repeat
p := Length(T);
repeat
dec(p);
until S[p] = '\';
T := copy(T,1,p);
until pos(T,S) = 1;
Edit1.Text := T;
end;
end;
end;

Am Ende steht das Ergebnis im Edit1.

Warnung:
Auf Endlos-Schleifen und Plausibilität usw. mußt Du selbst prüfen!

Gruß
j/\a
--
Hans-Peter Diettrich
2015-06-06 14:02:50 UTC
Permalink
Post by Lothar Planitzer
Aus dieser Liste, die bis zu 10000 Elemente enthalten kann, soll der
gemeinsame Teilpfad, in diesem Fall
G:\Audio\Itunes Mediatheken\iMainLib\iTunes Media\Music\
ermittelt werden.
Hat jemand eine Idee?
Ersten String nehmen, mit allen anderen Strings vergleichen und die
niedrigste Position des ersten ungleichen Zeichens merken. Um einen
Vergleich mit allen Strings wirst Du nicht herumkommen :-(

DoDi
Sieghard Schicktanz
2015-06-06 19:26:52 UTC
Permalink
Hallo Hans-Peter,
Post by Hans-Peter Diettrich
Ersten String nehmen, mit allen anderen Strings vergleichen und die
niedrigste Position des ersten ungleichen Zeichens merken. Um einen
Vergleich mit allen Strings wirst Du nicht herumkommen :-(
Kleine Verfeinerung: Bei jedem folgenden String nur noch die immer noch
gleichen Zeichen vergleichen (_nicht_ mit Copy (....)) - spart evtl. noch
bisserl Zeit, umso mehr, je kürzer der Rest ist.
Bei Restlänge 0 kann gleich abgebrochen werden.
--
--
(Weitergabe von Adressdaten, Telefonnummern u.ä. ohne Zustimmung
nicht gestattet, ebenso Zusendung von Werbung oder ähnlichem)
-----------------------------------------------------------
Mit freundlichen Grüßen, S. Schicktanz
-----------------------------------------------------------
Peter Below (TeamB)
2015-06-07 08:57:12 UTC
Permalink
Post by Lothar Planitzer
Hallo,
gegeben sei eine Liste von Dateinamen z.B.
G:\Audio\Itunes Mediatheken\iMainLib\iTunes Media\Music\Johnny
Burnette Trio\Honey Hush\17 All By Myself.m4a G:\Audio\Itunes
Mediatheken\iMainLib\iTunes Media\Music\The Everly Brothers\24
Original Classics\06 All I Have To Do Is Dream.m4a G:\Audio\Itunes
Mediatheken\iMainLib\iTunes Media\Music\Carl Perkins\All Shook Up\05
All Shook Up.m4a G:\Audio\Itunes Mediatheken\iMainLib\iTunes
Media\Music\Various Artists\Real Raw Rockabilly\1-17 All The Time.m4a
G:\Audio\Itunes Mediatheken\iMainLib\iTunes Media\Music\Chuck
Berry\Chuck Berry Is On Top\01 Almost Grown.m4a G:\Audio\Itunes
Mediatheken\iMainLib\iTunes Media\Music\Sun\Classic Carl Perkins
[Disc 5]\5-24 Anyway The Wind Blows [Single-V.m4a G:\Audio\Itunes
Mediatheken\iMainLib\iTunes Media\Music\Brenda Lee\Brenda Lee - All
Time Greatest Hits [Dis\10 As Usual.m4a
Aus dieser Liste, die bis zu 10000 Elemente enthalten kann, soll der
gemeinsame Teilpfad, in diesem Fall G:\Audio\Itunes
Mediatheken\iMainLib\iTunes Media\Music\ ermittelt werden.
Hat jemand eine Idee?
Leider hast Du uns nicht verraten, mit welcher Delphi-Version Du
arbeitest. Davon hängt aber ab, was so an nützlichen Funktionen
verfügbar ist...

Ich würde das in mehreren Etappen angehen.

1. Die Dateinamen abschneiden

var
LPathes: TStringlist;
I, K, LMinLength: integer;
begin
LPathes := TStringlist.Create;
LPathes.Sorted := true;
LPathes.Duplicates := dupIgnore;
for i:= 0 to filelist.count-1 do
LPathes.Add(ExtractFileDir(filelist[I]));

Damit haben wir eine Liste der Verzeichnisse und das sind vermutlich
schon deutlich weniger als es Files waren.
Wenn LPathes.Count = 1 ist hätten wir die Aufgabe sogar schon gelöst.

2.
Als nächstes müssen wir den kürzesten Pfad (mit den wenigsten Foldern,
nicht die Zahl der Zeichen!) in dieser Liste finden und alle längeren
auf diese Länge stutzen. Wenn wir mal annehmen, das alle Pfade mit
einem Laufwerksbuchstaben beginnen (wir also nicht mit Netzwerknamen
der Form \\server\share\..... fertig werden müssen) reicht es, einfach
die "\"-Zeichen zu zählen. Die ermittelte Länge bunkern wir in der
Objects-Eigenschaft der Stringliste.

LMinLength := Max(Integer);
LPathes.Sorted := false;
for I:= 0 to LPathes.Count-1 do begin
K:= LPathes[I].CountChar('\'); // requires TStringhelper!
// K:= CountOfChar('\', LPathes[I]);
LPathes.Objects[I] := Pointer(K);
if K < LMinLength then LMinLength := K;
end;
TruncatePathes(LPathes, LMinLength); // siehe unten

3.
Damit haben wir nun eine Liste mit Pfaden die alle die gleiche Länge
haben, und es sollte wieder weniger sein als vorher, da Dubletten schon
entfernt sind. Eventuell haben wir auch schon nur noch einen Pfad
übrig, und das ist der gesuchte. Falls nicht, wiederholen wir einfach
die Truncate-Operation mit einer geringeren Länge, bis nur noch ein
Pfad übrig ist:

while (LPathes.Count > 1) and (LMinLength > 1) do begin
Dec(LMinLength);
TruncatePathes(LPathes, LMinLength); // siehe unten
end;

Das sollte eigentlich funktionieren :-). Allerdings habe ich es nicht
ausprobiert und aller Kode in diesem Post wurde direkt in den
Messageeditor getippt und ist vollkommen ungetestet, nicht mal
compiliert.

// Helper functions:
function CountOfChar(ch: Char; const S: string): Cardinal;
var
i: Integer;
begin
Result := 0;
for i := 1 to Length(S) do
if S[i] = ch then
Inc(Result);
end;

procedure TruncatePathes(aPathlist: Tstringlist; aLength: integer);
var
LTemp: TStringlist;
I, K: integer;
S: string;
begin
LTemp:= TStringlist.Create;
try
LTemp.Sorted := true;
LTemp.Duplicates := dupIgnore;
for i:= 0 to aPathlist.Count-1 do begin
K := Integer(aPathlist.Objects[I]);
S := aPathlist[I];
while K > aLength do begin
S:= ExtractFileDir(S);
Dec(K);
end;
LTemp.Add(S);
end;
aPathlist.Assign(LTemp);
finally
LTemp.free
end;
end;
--
Peter Below (TeamB)
Lothar Planitzer
2015-06-07 10:15:56 UTC
Permalink
Post by Lothar Planitzer
Hallo,
gegeben sei eine Liste von Dateinamen z.B.
Aus dieser Liste, die bis zu 10000 Elemente enthalten kann, soll der gemeinsame
Teilpfad ermittelt werden.
Hallo,

Vielen Dank an alle für die nützlichen Tipps! Daraus habe ich folgende Lösung
entwickelt:

[snip]

Function CommonPath(const ML:TStringlist):string;
var
i, p: integer;
S, T: string;
begin
T:= '';
i:= 0;
S := ML[0]; // erstes Element der Liste
p := Length(S);
repeat
repeat // Dateiname bzw. letztes Unterverzeichnis 'kappen'
dec(p);
until (p < 1) or (S[p] = '\');
If p < 1 Then break; // kein gemeinsamer Pfad gefunden, Abbruch
setlength(S,p);
T:= S;
i:= 1;
While i < ML.count Do // Teilpfad in allen Listenelementen vergleichen
begin
if pos(S,ML[i]) <> 1 Then // bei erster Abweichung Abbruch
begin
T:= '';
break;
end;
inc(i);
end;
until(i = ML.count); //Erstmals alle Elemente durchlaufen?
result:= T;
end;

[/snip]

Die Routine soll u.a. zum Verarbeiten von Playlisten verwendet werden. Wird kein
gemeinsamer Teilpfad gefunden, z.B. bei einer Liste mit relativen Pfaden, dann
wird ein leerer String zurückgegeben.
Bisher scheint alles korrekt zu funktionieren. Aber erfahrungsgemäß taucht
irgendwann eine Playlist auf, mit der irgendwas nicht funktioniert. Schau'n wir
mal ;-)
Als Gelegenheits- und Hobby-Programmierer verwende ich nach wie vor Delphi 4.
Natürlich stoße ich immer häufiger an dessen Grenzen. Es juckt mir zwar immer
wieder in den Fingern, aber ich kann mich letztlich doch nicht entschließen, für
die sporadischen Einsätze mehrere hundert € für ein aktuelles Delphi auszugeben :-(

Gruß Lothar
Jakob Achterndiek
2015-06-07 11:15:14 UTC
Permalink
Am 07.06.2015, 12:15 Uhr, schrieb Lothar Planitzer
Post by Lothar Planitzer
Hallo,
Vielen Dank an alle für die nützlichen Tipps! Daraus habe ich
[..]
Danke für die Nachricht, Glückwunsch zum Erfolg - und eine Frage
in die Runde:
Ich selbst arbeite seit Anno Tobak mit Delphi 5 prof und möchte
eigentlich noch mal ein bißchen aufsteigen, vor allem will ich
dabei von ANSI auf utf8 und von MS Access auf mySQL umsteigen.
Erste Anläufe mit Lazarus und Delphi XE8 waren bisher wenig
erfolgreich.
Meine Frage: Ist da eher Lazarus oder eher XE8 oder noch ganz was
Anderes zu empfehlen?

Gruß
j/\a
--

Lesen Sie weiter auf narkive:
Loading...