Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Returning a Relative Path in .NET


:P
On this page:

I’ve been working on a generic configuration utility for Web applications (more on this in a few days) that is meant to simplify the process of taking an ASP. Net application and configuring the typical, external OS and Web Server things you need to do set up a Web application: Creating a virtual, setting file permissions, setting up VS.NET Solution files, configure MSDE if necessary). I give out sample apps all the time and I'm tired of writing README.HTM files to describe how to open the application and solution and getting support calls <g>... Anyway...

 

This utility stores information in an XML Serialized format from an object that contains a number of paths like the directory for the virtual to create, any directories for permissions to be set etc. and in order to make this generic these paths need to be stored inside of the serialized file as Relative paths so it’s easy to transport this data around. The Path class has a number of useful path related functions like GetFullPath() (which does the opposite: Takes a relative path and converts it into a full path), but unfortunately no RelativePath function.

 

So I ended up writing one:

 

///

/// Returns a relative path string from a full path.

///

/// The path to convert. Can be either a file or a directory

/// The base path to truncate to and replace

///

/// Lower case string of the relative path. If path is a directory it's returned

/// without a backslash at the end.

///

/// Examples of returned values:

///  .\test.txt, ..\test.txt, ..\..\..\test.txt, ., ..

///

public static string GetRelativePath(string FullPath, string BasePath )

{

      // *** Start by normalizing paths

      FullPath = FullPath.ToLower();

      BasePath = BasePath.ToLower();

 

      if ( BasePath.EndsWith("\\") )

            BasePath = BasePath.Substring(0,BasePath.Length-1);

      if ( FullPath.EndsWith("\\") )

            FullPath = FullPath.Substring(0,FullPath.Length-1);

 

      // *** First check for full path

      if ( FullPath.IndexOf(BasePath) > -1)

            return  FullPath.Replace(BasePath,".");

 

      // *** Now parse backwards

      string BackDirs = "";

      string PartialPath = BasePath;

      int Index = PartialPath.LastIndexOf("\\");

      while (Index > 0)

      {

            // *** Strip path step string to last backslash

            PartialPath = PartialPath.Substring(0,Index );

           

            // *** Add another step backwards to our pass replacement

            BackDirs = BackDirs + "..\\" ;

 

            // *** Check for a matching path

            if ( FullPath.IndexOf(PartialPath) > -1 )

            {

                  if ( FullPath == PartialPath )

                        // *** Full Directory match and need to replace it all

                        return FullPath.Replace(PartialPath,
                                        BackDirs.Substring(0,BackDirs.Length-1) );

                  else

                        // *** We're dealing with a file or a start path

                        return FullPath.Replace(PartialPath+

                                 (FullPath == PartialPath ?  "" : "\\"),BackDirs);

            }

            Index = PartialPath.LastIndexOf("\\",PartialPath.Length-1);

      }

 

      return FullPath;

}

 

I haven’t tested this through all the possible scenarios but it works well for both files and paths inside of a path tree.

 

As I was finishing this code, Mattias Sjoergen pointed out there’s an API call for this:

 

PathRelativePathTo

 

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/shlwapi/path/pathrelativepathto.asp

 

so you could use COM interop to talk to this as well if security of the app is not a concern. This function is a little more work though because you have to figure out first whether you are dealing with a path or a directory, while the code above works with either.

 

Relative paths are handy to have any time you are interested in storing configuration paths that need to travel across machines. If you let users pick filenames it's often a good idea to truncate  those filenames into relative paths when you store them...

 

 


The Voices of Reason


 

pspet
December 14, 2004

# re: Returning a Relative Path in .NET

Not works for FullPath="c:\aaaa\b.txt" BasePath="c:\aa"

> // *** First check for full path
> if ( FullPath.IndexOf(BasePath) > -1)
> return FullPath.Replace(BasePath,".");


Amo
November 04, 2005

# re: Returning a Relative Path in .NET

A fix for thsi situation would be

if (FullPath.IndexOf(BasePath) > -1) {
string temp = FullPath.Remove(0,BasePath.Length);
if (temp.IndexOf(@"\") < 1){
return FullPath.Replace(BasePath,".");
}
This code makes sure that we actually end at a complete dirName and not a incomplete dirName
so we get at C:\aaaa\b.txt C:\aa will result in ..\aaaa\b.txt and not .aa\b.txt

}

George van den Driessche
November 23, 2005

# re: Returning a Relative Path in .NET

Just convert to a Uri and use MakeRelative(). It's a two-liner.

kimdi
February 22, 2006

# re: Returning a Relative Path in .NET

In following code-list,
// *** We're dealing with a file or a start path
return FullPath.Replace(PartialPath+ (FullPath == PartialPath ? "" : "\\"),BackDirs);

(FullPath == PartialPath ? "" : "\\") is redundant-condition.

Emilio
October 24, 2008

# re: Returning a Relative Path in .NET

The Uri comment is incorrect, you can only use the Uri class for web paths and not for regular file system paths.

Rob Vesse
August 14, 2009

# Using Uri.MakeRelative with File URIs

You can use Uri.MakeRelative() fine with File paths you just have to use file scheme URIs like so:

public static string GetRelativePath(string FullPath, string BasePath) {
  if (!Path.IsPathRooted(FullPath)) FullPath = Path.GetFullPath(FullPath);
  if (!Path.IsPathRooted(BasePath)) BasePath = Path.GetFullPath(BasePath);

  if (!BasePath.EndsWith("\\")) BasePath += "\\";

  Uri temp = new Uri("file:///" + FullPath);
  Uri base = new Uri("file:///" + BasePath);
  return base.MakeRelative(temp);
}


MakeRelative() is deprecated so you should ideally use MakeRelativeUri() to get a Uri and do ToString() on it to get a string result

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2025