Convert absolute path to relative path
Today I needed to convert an absolute path to a relative path based on a specified base path. E.g.
c:\a\b\c -> c:\a\b\c\d\file.txt = d\file.txt
c:\a\b\c -> c:\a\file.txt = ..\..\file.txt
c:\a\b\c -> c:\a\x\file.txt = ..\..\x\file.txt
I am surprised there is nothing in the .NET framework so I had a hunt around and converted the code from the following URL (http://www.vergentsoftware.com/blogs/ckinsman/default.aspx?date=2006-08-07) into C#....
c:\a\b\c -> c:\a\b\c\d\file.txt = d\file.txt
c:\a\b\c -> c:\a\file.txt = ..\..\file.txt
c:\a\b\c -> c:\a\x\file.txt = ..\..\x\file.txt
I am surprised there is nothing in the .NET framework so I had a hunt around and converted the code from the following URL (http://www.vergentsoftware.com/blogs/ckinsman/default.aspx?date=2006-08-07) into C#....
private string RelativePath(string absolutePath, string relativeTo)
{
string[] absoluteDirectories = absolutePath.Split('\\');
string[] relativeDirectories = relativeTo.Split('\\');
//Get the shortest of the two paths
int length = absoluteDirectories.Length < relativeDirectories.Length ? absoluteDirectories.Length : relativeDirectories.Length;
//Use to determine where in the loop we exited
int lastCommonRoot = -1;
int index;
//Find common root
for (index = 0; index < length; index++)
if (absoluteDirectories[index] == relativeDirectories[index])
lastCommonRoot = index;
else
break;
//If we didn't find a common prefix then throw
if (lastCommonRoot == -1)
throw new ArgumentException("Paths do not have a common base");
//Build up the relative path
StringBuilder relativePath = new StringBuilder();
//Add on the ..
for (index = lastCommonRoot + 1; index < absoluteDirectories.Length; index++)
if (absoluteDirectories[index].Length > 0)
relativePath.Append("..\\");
//Add on the folders
for (index = lastCommonRoot + 1; index < relativeDirectories.Length - 1; index++)
relativePath.Append(relativeDirectories[index] + "\\");
relativePath.Append(relativeDirectories[relativeDirectories.Length - 1]);
return relativePath.ToString();
}
Comments
http://www.codeplex.com/FileDirectoryPath
public static String convertToRelativePath(String absolutePath, String relativeTo) {
StringBuilder relativePath = null;
// Thanks to:
// http://mrpmorris.blogspot.com/2007/05/convert-absolute-path-to-relative-path.html
absolutePath = absolutePath.replaceAll("\\\\", "/");
relativeTo = relativeTo.replaceAll("\\\\", "/");
if (absolutePath.equals(relativeTo) == true) {
} else {
String[] absoluteDirectories = absolutePath.split("/");
String[] relativeDirectories = relativeTo.split("/");
//Get the shortest of the two paths
int length = absoluteDirectories.length < relativeDirectories.length ?
absoluteDirectories.length : relativeDirectories.length;
//Use to determine where in the loop we exited
int lastCommonRoot = -1;
int index;
//Find common root
for (index = 0; index < length; index++) {
if (absoluteDirectories[index].equals(relativeDirectories[index])) {
lastCommonRoot = index;
} else {
break;
//If we didn't find a common prefix then throw
}
}
if (lastCommonRoot != -1) {
//Build up the relative path
relativePath = new StringBuilder();
//Add on the ..
for (index = lastCommonRoot + 1; index < absoluteDirectories.length; index++) {
if (absoluteDirectories[index].length() > 0) {
relativePath.append("../");
}
}
for (index = lastCommonRoot + 1; index < relativeDirectories.length - 1; index++) {
relativePath.append(relativeDirectories[index] + "/");
}
relativePath.append(relativeDirectories[relativeDirectories.length - 1]);
}
}
return relativePath == null ? null : relativePath.toString();
}
QString RelativePath( QString absolutePath, QString relativeTo, bool bIsFile /*= false*/ )
{
QStringList absoluteDirectories = absolutePath.split( '/', QString::SkipEmptyParts );
QStringList relativeDirectories = relativeTo.split( '/', QString::SkipEmptyParts );
//Get the shortest of the two paths
int length = absoluteDirectories.count() < relativeDirectories.count() ? absoluteDirectories.count() : relativeDirectories.count();
//Use to determine where in the loop we exited
int lastCommonRoot = -1;
int index;
//Find common root
for (index = 0; index < length; index++)
if (absoluteDirectories[index] == relativeDirectories[index])
lastCommonRoot = index;
else
break;
//If we didn't find a common prefix then throw
if (lastCommonRoot == -1)
throw QString("Paths do not have a common base");
//Build up the relative path
QString relativePath;
//Add on the ..
for (index = lastCommonRoot + 1; index < absoluteDirectories.count() - (bIsFile?1:0); index++)
if (absoluteDirectories[index].length() > 0)
relativePath.append("../");
//Add on the folders
for (index = lastCommonRoot + 1; index < relativeDirectories.count() - 1; index++)
relativePath.append( relativeDirectories[index] ).append( "/" );
relativePath.append(relativeDirectories[relativeDirectories.count() - 1]);
return relativePath;
}
//Add on the ..
for (index = lastCommonRoot + 1; index < relativeDirectories.Length; index++)
if (relativeDirectories[index].Length > 0)
relativePath.Append("..\\");
//Add on the folders
for (index = lastCommonRoot + 1; index < absoluteDirectories.Length - 1; index++)
relativePath.Append(absoluteDirectories[index] + "\\");
relativePath.Append(absoluteDirectories[absoluteDirectories.Length - 1]);
function GetRelativePath(RelativeTo, AbsolutePath: String): String;
procedure SplitDirectories(Directory: String; const Directories: TStringList);
var
i: Integer;
begin
for i := length(Directory) downto 1 do
begin
if (Directory[i] = '\') or (i = 1) then
begin
Directories.Insert(0, copy(Directory, i + 1, MaxInt));
Directory := copy(Directory, 1, i - 1);
end;
end;
end;
var
AbsoluteDirectories,
RelativeDirectories: TStringList;
Len, LastCommonRoot, i: Integer;
begin
Result := '';
AbsoluteDirectories := TStringList.Create;
RelativeDirectories := TStringList.Create;
try
SplitDirectories(AbsolutePath, AbsoluteDirectories);
SplitDirectories(RelativeTo, RelativeDirectories);
//Get the shortest of the two paths
Len := AbsoluteDirectories.Count;
if Len < RelativeDirectories.Count then
Len := RelativeDirectories.Count;
//Use to determine where in the loop we exited
LastCommonRoot := -1;
//Find common root
for i := 0 to Len do
begin
if AbsoluteDirectories[i] = RelativeDirectories[i] then
LastCommonRoot := i
else
break;
end;
//If we didn't find a common prefix then throw exception
if lastCommonRoot = -1 then
raise Exception.Create('Paths do not have a common base');
//Build up the relative path
//Add on the ..
for i := LastCommonRoot + 1 to pred(AbsoluteDirectories.Count) do
if length(AbsoluteDirectories[i]) > 0 then
Result := concat(Result, '..\');
//Add on the folders
for i := LastCommonRoot + 1 to pred(RelativeDirectories.Count) do
Result := concat(Result, RelativeDirectories[i], '\');
finally
// Free the stringlists
FreeAndNil(AbsoluteDirectories);
FreeAndNil(RelativeDirectories);
end;
end;
string relativePath = Path.Combine(pathToRelate, absolutePath)
Examples:
c:\a\b\c -> c:\a\b\c\d\file.txt = d\file.txt
Pseudo code:
using System.IO;
Console.Write(Path.Combine("c:\a\b\c\d\file.txt", "c:\a\b\c");
Result:
c:\a\b\c
This is not a relative path from c:\a\b\c to c:\a\b\c\d\file.txt which is d\file.txt.
will give you "../images/img.jpg"
[DllImport("shlwapi.dll", CharSet=CharSet.Auto)]
static extern bool PathRelativePathTo(
[Out] StringBuilder pszPath,
[In] string pszFrom,
[In] FileAttributes dwAttrFrom,
[In] string pszTo,
[In] FileAttributes dwAttrTo
);
( note: contains Berndwilli fix )
bool StringEQ( const std::string& str0, const std::string& str1 )
{
if ( str0.size() != str1.size() )
return false;
return ( strcmp( str0.c_str(), str1.c_str() ) == 0 );
}
// 7.3: http://www.oopweb.com/CPP/Documents/CPPHOWTO/Volume/C++Programming-HOWTO-7.html
//
void Tokenize(const std::string& str, std::vector& tokens, const std::string& delimiters)
{
// Skip delimiters at beginning.
std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
// Find first "non-delimiter".
std::string::size_type pos = str.find_first_of(delimiters, lastPos);
while (std::string::npos != pos || std::string::npos != lastPos)
{
// Found a token, add it to the vector.
tokens.push_back(str.substr(lastPos, pos - lastPos));
// Skip delimiters. Note the "not_of"
lastPos = str.find_first_not_of(delimiters, pos);
// Find next "non-delimiter"
pos = str.find_first_of(delimiters, lastPos);
}
}
//
// "c:\a\b\c" + "c:\a\x\file.txt" => "..\..\x\file.txt"
// http://mrpmorris.blogspot.com/2007/05/convert-absolute-path-to-relative-path.html
//
std::string ConvertAbsoluteToRelative( const std::string& absolutePath, const std::string& relativeTo )
{
std::vector< std::string > absoluteDirectories;
std::vector< std::string > relativeDirectories;
Tokenize( absolutePath, absoluteDirectories, "/" );
Tokenize( relativeTo, relativeDirectories, "/" );
// Get the shortest of the two paths
int length = absoluteDirectories.size() < relativeDirectories.size() ? absoluteDirectories.size() : relativeDirectories.size();
// Use to determine where in the loop we exited
int lastCommonRoot = -1;
int index;
// Find common root
for (index = 0; index < length; index++)
if (StringEQ( absoluteDirectories[index], relativeDirectories[index]))
lastCommonRoot = index;
else
break;
// If we didn't find a common prefix then throw
if (lastCommonRoot == -1)
{
assert( 0 ); // "Paths do not have a common base"
return "";
}
std::string relativePath;
// Add on the ..
for (index = lastCommonRoot + 1; index < relativeDirectories.size(); index++)
if (relativeDirectories[index].size() > 0)
relativePath += "../";
// Add on the folders
for (index = lastCommonRoot + 1; index < absoluteDirectories.size() - 1; index++)
{
relativePath += absoluteDirectories[index];
relativePath += "/";
}
relativePath += absoluteDirectories[absoluteDirectories.size() - 1];
// :)
return relativePath;
}
If you just want a path relative to the current directory, use Path.GetFullPath(relativePath).
Path.GetFullPath(".") == Directory.GetCurrentDirectory()
Need PHP Version, here it is
function RelativePath($absolutePath, $relativeTo)
{
$absoluteDirectories = explode('/',$absolutePath);
$relativeDirectories = explode('/',$relativeTo);
//Get the shortest of the two paths
$length = count($absoluteDirectories) < count($relativeDirectories) ? count($absoluteDirectories) : count($relativeDirectories);
//Use to determine where in the loop we exited
$lastCommonRoot = -1;
$index;
//Find common root
for ($index = 0; $index < $length; $index++)
if ($absoluteDirectories[$index] == $relativeDirectories[$index])
$lastCommonRoot = $index;
else
break;
//If we didn't find a common prefix then throw
if ($lastCommonRoot == -1)
return "Paths do not have a common base";
//Build up the relative path
//StringBuilder relativePath = new StringBuilder();
$relativePath = "";
//Add on the ..
for ($index = $lastCommonRoot + 1; $index < count($absoluteDirectories); $index++)
if (strlen($absoluteDirectories[$index]) > 0)
$relativePath .= "..\\";
//Add on the folders
for ($index = $lastCommonRoot + 1; $index < count($relativeDirectories) - 1; $index++)
$relativePath .= $relativeDirectories[$index]."\\";
$relativePath .= $relativeDirectories[count($relativeDirectories) - 1];
return $relativePath;
}
eg.: C:\a\b -> C:\a = ..\a
It should be ..\
To correct that i made a change:
int lastPiece = relativeDirectories.length - 1;
if (lastPiece > lastCommonRoot) {
relativePath.append(relativeDirectories[lastPiece]);
}
return relativePath.toString();
The code is Java, but is easy to convert as you can see.
eg.: C:\a\b -> C:\a = ..\a
It should be ..\
To correct that i made a change:
int lastPiece = relativeDirectories.length - 1;
if (lastPiece > lastCommonRoot) {
relativePath.append(relativeDirectories[lastPiece]);
}
return relativePath.toString();
The code is Java, but is easy to convert as you can see.