Seite 1 von 4

Smarte Methode zum Dateipfad kürzen gesucht

Verfasst: 08.03.2020 17:23
von Kurzer
Moin zusammen,

gibt es in PB eine clevere Methode (unter Windows), um von einer Verzeichnisebene (als String) die n-te übergeordnete Verzeichnisebene anzusprechen?

Angenommen ich habe das Verzeichnis D:\Obst\Äpfel\Reife\Greenstar\ ermittelt und möchte auf D:\Obst\Äpfel\Reife\ mittels Delete() oder Rename() zugreifen.

Muss ich den String dann zwingend vorher kürzen (StringField, CountString) oder gibt es sowas wie "\.." für ein Verzeichnis höher? Meine Tests in diese Richtung haben bisher leider nicht funktioniert.

Kurzer

Re: Smarte Methode zum Dateipfad kürzen gesucht

Verfasst: 08.03.2020 18:49
von mk-soft
Ohne Worte :wink:

Code: Alles auswählen

;-TOP by mk-soft ;)

Procedure.s GetParentPath(Path.s, n = 1)
  For i = 1 To n
    If Right(Path, 1) = #PS$
      Path = Left(Path, Len(Path) - 1)
    EndIf
    Path = GetPathPart(Path)
  Next
  ProcedureReturn Path
EndProcedure

Debug GetParentPath("D:\Obst\Äpfel\Reife\Greenstar\")
Debug GetParentPath("D:\Obst\Äpfel\Reife\Greenstar\", 2)
Debug GetParentPath("D:\Obst\Äpfel\Reife\Greenstar\", 3)
Debug GetParentPath("D:\Obst\Äpfel\Reife\Greenstar\", 4)


Re: Smarte Methode zum Dateipfad kürzen gesucht

Verfasst: 08.03.2020 19:23
von Kurzer
Das ist smart! :allright:

Wenn ich mir da mein Konstrukt ansehe... :lol:

Code: Alles auswählen

EnableExplicit

Procedure.s PathPart (sPath.s, iLevelsUp.i)
	#Separator = "\"
	Protected.i i, iOutPathLen, iPathLen = Len(sPath), iCountLevels
	Protected.s sTemp = ReplaceString(sPath, "/", #Separator)
	
	; Pfad und übergeordnete Verzeichnisebene prüfen
	If iPathLen = 0 Or iLevelsUp < 1 : ProcedureReturn sPath : EndIf
	
	If Right(sTemp, 1) = #Separator
		sTemp = Left(sTemp, iPathLen - 1)
	EndIf
	
	iCountLevels = CountString(sTemp, #Separator) + 1
	
	; Anzahl der Unterverzeichnisse im Pfad prüfen
	If iCountLevels = 1
		ProcedureReturn sPath
	ElseIf iLevelsUp > iCountLevels
		ProcedureReturn ""
	EndIf
	
	For i = 1 To iCountLevels - iLevelsUp
		iOutPathLen + Len(StringField(sTemp, i, #Separator)) + 1 ; + 1 für den Separator
	Next i
	
	ProcedureReturn Left(sPath, iOutPathLen)
EndProcedure

Debug PathPart("D:\Computer\Level1/Level2\Level3\", 0)
Debug PathPart("D:/Computer\Level1\Level2/Level3", 1)
Debug PathPart("D:/Computer/Level1/Level2/Level3", 2)
Wobei ich aber auch gemischte Path-Separatoren unterstützen wollte.

Vielen Dank für dein klasse Beispiel, mk-soft.

Re: Smarte Methode zum Dateipfad kürzen gesucht

Verfasst: 09.03.2020 12:39
von NicTheQuick
Für Windows gibt es auch noch eine schöne Funktion, die alle ".." und "." auflöst:

Code: Alles auswählen

Procedure.s canonicalizePath(path.s)
	Protected newpath.s = Space(#MAX_PATH)
	If PathCanonicalize_(@newpath, @path)
		ProcedureReturn newpath
	EndIf
	ProcedureReturn ""
EndProcedure

Debug canonicalizePath("C:\Windows\system32\..\..\Program Files")
Damit könnte man notfalls auch noch arbeiten.

Re: Smarte Methode zum Dateipfad kürzen gesucht

Verfasst: 09.03.2020 18:06
von Nino
@mk-soft:
Schöner Code (funktioniert allerdings nur mit absoluten Pfaden).
Er kann noch etwas vereinfacht werden. :-)

Code: Alles auswählen

Procedure.s GetParentPath(Path.s, n.i=1)
   Protected i.i
   
   For i = 1 To n
      Path = GetPathPart(RTrim(Path, #PS$))
   Next
   
   ProcedureReturn Path
EndProcedure


Debug GetParentPath("D:\Obst\Äpfel\Reife\Greenstar\")
Debug GetParentPath("D:\Obst\Äpfel\Reife\Greenstar\", 2)
Debug GetParentPath("D:\Obst\Äpfel\Reife\Greenstar\", 3)
Debug GetParentPath("D:\Obst\Äpfel\Reife\Greenstar\", 4)

Re: Smarte Methode zum Dateipfad kürzen gesucht

Verfasst: 09.03.2020 18:29
von Nino
Kurzer hat geschrieben:Wobei ich aber auch gemischte Path-Separatoren unterstützen wollte.
Dazu braucht man nur die If-Abfrage ein wenig zu ändern.
(Eine Prüfung mit If ist übrigens nicht nur hier in meiner Variante, sondern auch in mk-softs Original-Code nur 1x vor der Schleife nötig. Innerhalb der Schleife braucht man das nicht zu prüfen, weil da am Ende immer ein Pfadtrenner ist.)

Code: Alles auswählen

Procedure.s GetParentPath(Path.s, n.i=1)
   Protected i.i
   
   If FindString("\/", Right(Path, 1)) = 0
      Path + #PS$
   EndIf
   
   For i = 1 To n
      Path = GetPathPart(Left(Path, Len(Path)-1))
   Next
   
   ProcedureReturn Path
EndProcedure


Debug GetParentPath("D:\Obst\Äpfel\Reife\Greenstar\")
Debug GetParentPath("D:\Obst\Äpfel\Reife\Greenstar\", 2)
Debug GetParentPath("D:/Obst/Äpfel/Reife/Greenstar/", 3)
Debug GetParentPath("D:\Obst\Äpfel\Reife\Greenstar\", 4)

Re: Smarte Methode zum Dateipfad kürzen gesucht

Verfasst: 09.03.2020 20:36
von Sicro
Im CodeArchiv habe ich dafür auch ein Include: GetParentDirectory.pbi

Re: Smarte Methode zum Dateipfad kürzen gesucht

Verfasst: 09.03.2020 20:55
von mk-soft
Gemischter Path Separator ist eigentliche nicht erforderlich.
#PS$ liefert je nach OS den richtigen Separator.

@Nino
Mist... RTrim hatte ich vergessen :allright:

Re: Smarte Methode zum Dateipfad kürzen gesucht

Verfasst: 09.03.2020 21:51
von Nino
mk-soft hat geschrieben:Gemischter Path Separator ist eigentliche nicht erforderlich.
#PS$ liefert je nach OS den richtigen Separator.
Ja, das schon. Und unter Linux funktioniert sowieso nur "/" als Pfadtrenner.
Aber unter Windows kann ein Pfad auch mal den falschen Pfadtrenner "/" enthalten, oder evtl. beide gemischt: "C:\Ein/Verzeichnis". Es kommt darauf an woher der Inhalt der Variable 'Pfad' stammt. Wenn es sich z.B. um eine Benutzereingabe handelt, weiß man vorher nie was da drin ist. :-)
mk-soft hat geschrieben:@Nino
Mist... RTrim hatte ich vergessen :allright:
:-)
Ja, dadurch wird der Code etwas kürzer. Allerdings käme es mir im Endeffekt darauf an, ob eine Variante deutlich schneller ist (habe das nicht getestet).

Re: Smarte Methode zum Dateipfad kürzen gesucht

Verfasst: 10.03.2020 19:22
von Kurzer
:allright:

Herzlichen Dank für eure weiteren Anregungen, Nino, Sicro und Nic.
Ich habe mich nun für diese Lösung entschieden die optional auch die Pfadseparatoren korrigert.

Code: Alles auswählen

Procedure.s GetParentPath(sPath.s, iLevelsUp.i = 1, sNewSeparator.s = "")
	Protected i.i, sOldSeparator.s = ""
	
	If FindString("\/", Right(sPath, 1)) = 0
		sPath + #PS$
	EndIf
	
	For i = 1 To iLevelsUp
		sPath = GetPathPart(Left(sPath, Len(sPath) - 1))
	Next
	
	Select sNewSeparator
		Case "\"
			sOldSeparator = "/" 
			ReplaceString(sPath, sOldSeparator, sNewSeparator, #PB_String_InPlace)
		Case "/"
			sOldSeparator = "\"
			ReplaceString(sPath, sOldSeparator, sNewSeparator, #PB_String_InPlace)
	EndSelect
	
	ProcedureReturn sPath
EndProcedure

Debug GetParentPath("D:\1\Obst/Äpfel\Reife/Greenstar",2, #PS$)
Debug GetParentPath("D:/2/Obst/Äpfel/Reife/Greenstar/",2, #PS$)
Debug GetParentPath("D:\3\Obst/Äpfel\Reife/Greenstar\", 3)
Debug GetParentPath("D:\4\Obst\Äpfel\Reife\Greenstar\", 3)
Debug GetParentPath("D:\5/Obst\Äpfel\Reife\Greenstar\")
Bei der Erstellung ist mir bzgl. ReplaceString() bei Benutzung des Parameters #PB_String_InPlace ein kurioses Verhalten aufgefallen.

Die Hilfe sagt zu dem Parameter:
Wenn diese Option verwendet wird, muss das Ergebnis von ReplaceString() ignoriert werden (und auch nirgends zugewiesen - da es der als Parameter übergebene 'String$' ist, der geändert wird).
Aber offenbar gibt ReplaceString() bei Verwendung von #PB_String_InPlace statt des übergebenen Strings einen alten Stringbuffer zurück, der bei der letzten Benutzung von ReplaceString() gefüllt wurde.

Code: Alles auswählen

Procedure.s ReturnString()
	Protected sText.s
	sText = "Eiscreme mit Erdbeersauce"
	sText = ReplaceString(sText, "Erd", "Brom")
	ProcedureReturn sText
EndProcedure

Debug ReturnString() ; Gibt 'Eiscreme mit Brombeersauce' aus
Debug ReplaceString("Hallo Welt", "Welt", "Mond", #PB_String_InPlace) ; Gibt ebenfalls 'Eiscreme mit Brombeersauce' aus!
Debug ReplaceString("Hallo Welt", "Welt", "Mond")                     ; Gibt 'Hallo Mond' aus