AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

[Func] RelativePath & AbsolutePath
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
toralf



Joined: 31 Jan 2005
Posts: 3842
Location: Bremen, Germany

PostPosted: Thu May 24, 2007 4:13 pm    Post subject: [Func] RelativePath & AbsolutePath Reply with quote

Two functions that return the relative path of a Slavepath relative to a Masterpath or the aboslute path of a RelativePath to a AbsolutePath. A separator s allows to adjust for Unix/HTTP paths.
Code:
RelativePath(MasterPath, SlavePath, s="\")
AbsolutePath(AbsolutPath, RelativePath, s="\")


Titan and I improved both functions for speed and treatment of hopefully all cases. These cases are
Quote:
../data --> the folder data is located one directory higher
./data --> the folder data is located in the same directory
/data --> the folder data is located in the root of the server or drive


To expand complex paths like "./.././/data/get.html", Titan created a function that recurse.

RelativePath
Code:
RelativePath(MasterPath, SlavePath, s="\"){
    len := InStr(MasterPath, s, "", InStr(MasterPath, s . s) + 2) ;get server or drive string length
    If SubStr(MasterPath, 1, len ) <> SubStr(SlavePath, 1, len )  ;different server or drive
        Return SlavePath                                              ;return absolut path
    MasterPath := SubStr(MasterPath, len + 1 )                    ;remove server or drive from MasterPath
    SlavePath := SubStr(SlavePath, len + 1 )                      ;remove server or drive from SlavePath
    If InStr(MasterPath, s, "", 0) = StrLen(MasterPath)           ;remove last \ from MasterPath if any
        StringTrimRight, MasterPath, MasterPath, 1
    If InStr(SlavePath, s, "", 0) = StrLen(SlavePath)             ;remove last \ from SlavePath if any
        StringTrimRight, SlavePath, SlavePath, 1
    Loop{
        If !MasterPath                                            ;when there is nothing didentical
            Return s SlavePath                                         ;return SlavePath in root
        If InStr(SlavePath s, MasterPath s){                      ;when parts of paths match
            If !r                                                      ;no relative part yet
                r = .%s%                                                   ;SlavePath is in the MasterPath
            Return r . SubStr(SlavePath,StrLen(MasterPath) + 2)        ;return relative path
        }Else{                                                    ;otherwise
            r .= ".." s                                                ;add relative part
            MasterPath := SubStr(MasterPath, 1, InStr(MasterPath, s, "", 0) - 1)   ;remove one folder from MasterPath
          }
      }
  }


AbsolutePath
Code:
AbsolutePath(AbsolutPath, RelativePath, s="\") {
    len := InStr(AbsolutPath, s, "", InStr(AbsolutPath, s . s) + 2) - 1   ;get server or drive string length
    pr := SubStr(AbsolutPath, 1, len)                                     ;get server or drive name
    AbsolutPath := SubStr(AbsolutPath, len + 1)                           ;remove server or drive from AbsolutPath
    If InStr(AbsolutPath, s, "", 0) = StrLen(AbsolutPath)                 ;remove last \ from AbsolutPath if any
        StringTrimRight, AbsolutPath, AbsolutPath, 1
    If InStr(RelativePath, s, "", 0) = StrLen(RelativePath)               ;remove last \ from RelativePath if any
        StringTrimRight, RelativePath, RelativePath, 1
    If InStr(RelativePath, s) = 1                                         ;when first char is \ go to AbsolutPath of server or drive
        AbsolutPath := "", RelativePath := SubStr(RelativePath, 2)            ;set AbsolutPath to nothing and remove one char from RelativePath
    Else If InStr(RelativePath,"." s) = 1                                 ;when first two chars are .\ add to current AbsolutPath directory
        RelativePath := SubStr(RelativePath, 3)                               ;remove two chars from RelativePath
    Else {                                                                ;otherwise
        StringReplace, RelativePath, RelativePath, ..%s%, , UseErrorLevel     ;remove all ..\ from RelativePath
        Loop, %ErrorLevel%                                                    ;for all ..\
            AbsolutPath := SubStr(AbsolutPath, 1, InStr(AbsolutPath, s, "", 0) - 1)  ;remove one folder from AbsolutPath
      }
    Return, pr . AbsolutPath . s . RelativePath                             ;concatenate server + AbsolutPath + separator + RelativePath
  }

_________________
Ciao
toralf


Last edited by toralf on Fri May 25, 2007 11:42 pm; edited 3 times in total
Back to top
View user's profile Send private message Send e-mail Visit poster's website
TheIrishThug



Joined: 19 Mar 2006
Posts: 370

PostPosted: Thu May 24, 2007 4:27 pm    Post subject: Reply with quote

Works nicely unless you have the following:
Quote:
Path1 = \\server.com\user\Files\Docs\Code\AHK\SciTEDirector\includes
Path2 = \\server.com\user123\Files\Docs\Code\AHK\SmartGui\no_commit\icons_dev


But it's an easy fix. Just break the loop when you find the first folder that doesn't match.
Code:
Path1 = \\server.com\user\Files\Docs\Code\AHK\SciTEDirector\includes
Path2 = \\server.com\user\Files\Docs\Code\AHK\SmartGui\no_commit\icons_dev
MsgBox, % RelativePath(Path1, Path2)
MsgBox, % RelativePath(Path2, Path1)
Path2 = \\server.com\user123\Files\Docs\Code\AHK\SmartGui\no_commit\icons_dev
MsgBox, % RelativePath(Path1, Path2)
MsgBox, % RelativePath(Path2, Path1)
Return

;return the relative path of Slavepath to Masterpath
RelativePath(MasterPath, SlavePath){
    ;remove last \ if there are any
    MasterPath := RegExReplace(MasterPath, "\\$")
    SlavePath  := RegExReplace(SlavePath, "\\$")

    ;create arrays
    StringSplit, MasterPath, MasterPath, \
    StringSplit, SlavePath, SlavePath, \
 
    ;sort out equivalent portions
    Loop, %MasterPath0%
        If (MasterPath%A_Index% = SlavePath%A_Index%)
            Same := A_Index
        Else
            Break
   
    ;build relative path       
    Loop, % MasterPath0 - Same
        RelativePath .= "..\"
   
    Loop, % SlavePath0 - Same {
        ID := Same + A_Index
        RelativePath .= SlavePath%ID% "\"   
      }
   
    Return RelativePath
  }
Back to top
View user's profile Send private message Visit poster's website AIM Address
toralf



Joined: 31 Jan 2005
Posts: 3842
Location: Bremen, Germany

PostPosted: Thu May 24, 2007 4:33 pm    Post subject: Reply with quote

Thanks, updated the first post.
_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
Titan



Joined: 11 Aug 2004
Posts: 5068
Location: imaginationland

PostPosted: Thu May 24, 2007 5:58 pm    Post subject: Reply with quote

You can do this with regex too:

Code:
RelPath(root, dir) {
   ex := RegExReplace(dir, "(\\+[^\\]+)", "($1)?")
   StringReplace, ex, ex, \, \\, All
   pos := InStr(root, rel := RegExReplace(root, ex))
   Return, RegExReplace(rel, "\\[^\\]+", "..\") . SubStr(dir, pos + 1)
}

_________________

RegExReplace("irc.freenode.net/ahk", "^(?=(.(?=[\0-r\[]*((?<=\.).))))(?:[c-\x73]{2,8}(\S))+((2)|\b[^\2-]){2}\D++$", "$u3$1$3$4$2")
Back to top
View user's profile Send private message Visit poster's website
toralf



Joined: 31 Jan 2005
Posts: 3842
Location: Bremen, Germany

PostPosted: Thu May 24, 2007 6:22 pm    Post subject: Reply with quote

Unfortunately the regex version doesn't work for all cases. Test code follows
Code:
Path1 = \\server.com\user\Files\Docs\Code\AHK\SciTEDirector\includes
Path2 = \\server.com\user\Files\Docs\Code\AHK\SmartGui\no_commit\icons_dev
MsgBox, % "1: " RelativePath(Path1, Path2) "`n2: " RelPath(Path1, Path2)
MsgBox, % "1: " RelativePath(Path2, Path1) "`n2: " RelPath(Path2, Path1)
MsgBox, % "1: " RelativePath(Path1, Path1) "`n2: " RelPath(Path1, Path1) ;< supposed to be empty
Path1 = \\server.com\user\Files\Docs\Code\AHK\SciTEDirector\includes
Path2 = \\server.com\user\Files123\Docs\Code\AHK\SmartGui\no_commit\icons_dev
MsgBox, % "1: " RelativePath(Path1, Path2) "`n2: " RelPath(Path1, Path2)
MsgBox, % "1: " RelativePath(Path2, Path1) "`n2: " RelPath(Path2, Path1)
MsgBox, % "1: " RelativePath(Path1, Path1) "`n2: " RelPath(Path1, Path1) ;< supposed to be empty
Return

_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
Titan



Joined: 11 Aug 2004
Posts: 5068
Location: imaginationland

PostPosted: Thu May 24, 2007 6:53 pm    Post subject: Reply with quote

That was just an example, proper regex would be:

Code:
RelPath(root, dir) {
   Loop, Parse, dir, \
      If A_LoopField
         ex .= "(\\" . (d ? "\\" : "") . A_LoopField, ey .= "\b)?", d := 0
      Else d = 1
   Return, (pos := InStr(root, rel := RegExReplace(root, ex . ey)))
      and rel ? RegExReplace(rel, "\\[^\\]+", "..\") . SubStr(dir, pos + 1) : ""
}

_________________

RegExReplace("irc.freenode.net/ahk", "^(?=(.(?=[\0-r\[]*((?<=\.).))))(?:[c-\x73]{2,8}(\S))+((2)|\b[^\2-]){2}\D++$", "$u3$1$3$4$2")
Back to top
View user's profile Send private message Visit poster's website
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Thu May 24, 2007 7:38 pm    Post subject: Reply with quote

Very useful, thank you.
_________________
Back to top
View user's profile Send private message MSN Messenger
toralf



Joined: 31 Jan 2005
Posts: 3842
Location: Bremen, Germany

PostPosted: Thu May 24, 2007 10:54 pm    Post subject: Reply with quote

Thanks for the update. It seems to work.
I tested both for performance, the regex version is slightly slower.
Since the normal code is easier to maintain and change to specific needs, I'll stick with it. Thanks again for showing the alternative.
_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
Titan



Joined: 11 Aug 2004
Posts: 5068
Location: imaginationland

PostPosted: Thu May 24, 2007 11:14 pm    Post subject: Reply with quote

A benchmark shows that regex is faster albeit slight fluctuations. However your code is better as it doesn't have the regex overhead, which is only justified for several thousands of calls. Thanks for creating this, it'll be useful to me.
_________________

RegExReplace("irc.freenode.net/ahk", "^(?=(.(?=[\0-r\[]*((?<=\.).))))(?:[c-\x73]{2,8}(\S))+((2)|\b[^\2-]){2}\D++$", "$u3$1$3$4$2")
Back to top
View user's profile Send private message Visit poster's website
Titan



Joined: 11 Aug 2004
Posts: 5068
Location: imaginationland

PostPosted: Fri May 25, 2007 4:43 pm    Post subject: Reply with quote

Sometimes you need to get the absolute path from a relative one, this function does just that:

Code:
MsgBox, % RelToAbs("\\server.com\user\Files\Docs\Code\AHK\SmartGui\no_commit\icons_dev", "..\..\..\SciTEDirector\includes\")
MsgBox, % RelToAbs("http://example.com/info/sec/", "../data/get.html", "/")
MsgBox, % RelToAbs("http://example.com/info/sec/", "/data/get.html", "/")

RelToAbs(root, dir, s = "\") {
   pr := SubStr(root, 1, len := InStr(root, s, "", InStr(root, s . s) + 2) - 1)
      , root := SubStr(root, len + 1), off := 0
   If InStr(root, s, "", 0) = StrLen(root)
      StringTrimRight, root, root, 1
   If InStr(dir, s, "", 0) = StrLen(dir)
      StringTrimRight, dir, dir, 1
   Loop, Parse, dir, %s%
      If InStr(A_LoopField, "..") = 1
         root := SubStr(root, 1, InStr(root, s, "", 0) - 1)
      Else If A_LoopField =
         root := "", dir := SubStr(dir, 2)
   StringReplace, dir, dir, ..%s%, , All
   Return, pr . root . s . dir
}

_________________

RegExReplace("irc.freenode.net/ahk", "^(?=(.(?=[\0-r\[]*((?<=\.).))))(?:[c-\x73]{2,8}(\S))+((2)|\b[^\2-]){2}\D++$", "$u3$1$3$4$2")
Back to top
View user's profile Send private message Visit poster's website
toralf



Joined: 31 Jan 2005
Posts: 3842
Location: Bremen, Germany

PostPosted: Fri May 25, 2007 8:14 pm    Post subject: Reply with quote

This would be my version for that problem:
Code:
AbsolutePath(root, dir, s = "\") {
    ;remove last \ if there are any
    root := RegExReplace(root, "\Q" s "\E$")
    dir  := RegExReplace(dir, "\Q" s "\E$")
    ;remove first \ of relativepath if there is one
    dir  := RegExReplace(dir, "^\Q" s "\E")

    ;create arrays
    StringSplit, root, root, %s%
    StringSplit, dir, dir, %s%

    ;combine to new absolute path
    Count = 0
    Loop % Root0 + dir0 {
        If (A_Index <= dir0) {
            ID := Dir0 - A_Index + 1
            If (Dir%ID% <> "..")
                AbsolutePath := Dir%ID% . s . AbsolutePath
            Else Count++
        }Else{
            ID := Root0 + Dir0 - A_Index + 1 - Count
            If ID > 0
                AbsolutePath := Root%ID% . s . AbsolutePath
            Else Break
          }
      }
    Return RegExReplace(AbsolutePath, "\Q" s "\E$") ;remove last \
  }
BTW: Our solutions differ for the third test example in your code. Which one is correct?
_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
Titan



Joined: 11 Aug 2004
Posts: 5068
Location: imaginationland

PostPosted: Fri May 25, 2007 8:29 pm    Post subject: Reply with quote

toralf wrote:
Our solutions differ for the third test example in your code. Which one is correct?
My one is correct, at least for URI standards. Besides, I thought you never liked regex.
_________________

RegExReplace("irc.freenode.net/ahk", "^(?=(.(?=[\0-r\[]*((?<=\.).))))(?:[c-\x73]{2,8}(\S))+((2)|\b[^\2-]){2}\D++$", "$u3$1$3$4$2")
Back to top
View user's profile Send private message Visit poster's website
toralf



Joined: 31 Jan 2005
Posts: 3842
Location: Bremen, Germany

PostPosted: Fri May 25, 2007 9:16 pm    Post subject: Reply with quote

I do like RegEx, but only when it is fast and necessary.

My code for the absolute path is much slower then yours. I currently look for speed ups.
_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
toralf



Joined: 31 Jan 2005
Posts: 3842
Location: Bremen, Germany

PostPosted: Fri May 25, 2007 9:18 pm    Post subject: Reply with quote

Titan wrote:
My one is correct, at least for URI standards. Besides, I thought you never liked regex.
Does a / infront mean that it is the root of the server?
_________________
Ciao
toralf
Back to top
View user's profile Send private message Send e-mail Visit poster's website
Titan



Joined: 11 Aug 2004
Posts: 5068
Location: imaginationland

PostPosted: Fri May 25, 2007 9:24 pm    Post subject: Reply with quote

toralf wrote:
Does a / infront mean that it is the root of the server?
Yes, / means root, ../ is one level up and ./ is the current directory (which neither of our functions cover).
_________________

RegExReplace("irc.freenode.net/ahk", "^(?=(.(?=[\0-r\[]*((?<=\.).))))(?:[c-\x73]{2,8}(\S))+((2)|\b[^\2-]){2}\D++$", "$u3$1$3$4$2")
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group