Getting path relative to the current working directory? [duplicate]

If you don't mind the slashes being switched, you could [ab]use Uri:

Uri file = new Uri(@"c:\foo\bar\blop\blap.txt");
// Must end in a slash to indicate folder
Uri folder = new Uri(@"c:\foo\bar\");
string relativePath = 
Uri.UnescapeDataString(
    folder.MakeRelativeUri(file)
        .ToString()
        .Replace('/', Path.DirectorySeparatorChar)
    );

As a function/method:

string GetRelativePath(string filespec, string folder)
{
    Uri pathUri = new Uri(filespec);
    // Folders must end in a slash
    if (!folder.EndsWith(Path.DirectorySeparatorChar.ToString()))
    {
        folder += Path.DirectorySeparatorChar;
    }
    Uri folderUri = new Uri(folder);
    return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar));
}

You can use Environment.CurrentDirectory to get the current directory, and FileSystemInfo.FullPath to get the full path to any location. So, fully qualify both the current directory and the file in question, and then check whether the full file name starts with the directory name - if it does, just take the appropriate substring based on the directory name's length.

Here's some sample code:

using System;
using System.IO;

class Program
{
    public static void Main(string[] args)
    {
        string currentDir = Environment.CurrentDirectory;
        DirectoryInfo directory = new DirectoryInfo(currentDir);
        FileInfo file = new FileInfo(args[0]);

        string fullDirectory = directory.FullName;
        string fullFile = file.FullName;

        if (!fullFile.StartsWith(fullDirectory))
        {
            Console.WriteLine("Unable to make relative path");
        }
        else
        {
            // The +1 is to avoid the directory separator
            Console.WriteLine("Relative path: {0}",
                              fullFile.Substring(fullDirectory.Length+1));
        }
    }
}

I'm not saying it's the most robust thing in the world (symlinks could probably confuse it) but it's probably okay if this is just a tool you'll be using occasionally.


public string MakeRelativePath(string workingDirectory, string fullPath)
{
    string result = string.Empty;
    int offset;

    // this is the easy case.  The file is inside of the working directory.
    if( fullPath.StartsWith(workingDirectory) )
    {
        return fullPath.Substring(workingDirectory.Length + 1);
    }

    // the hard case has to back out of the working directory
    string[] baseDirs = workingDirectory.Split(new char[] { ':', '\\', '/' });
    string[] fileDirs = fullPath.Split(new char[] { ':', '\\', '/' });

    // if we failed to split (empty strings?) or the drive letter does not match
    if( baseDirs.Length <= 0 || fileDirs.Length <= 0 || baseDirs[0] != fileDirs[0] )
    {
        // can't create a relative path between separate harddrives/partitions.
        return fullPath;
    }

    // skip all leading directories that match
    for (offset = 1; offset < baseDirs.Length; offset++)
    {
        if (baseDirs[offset] != fileDirs[offset])
            break;
    }

    // back out of the working directory
    for (int i = 0; i < (baseDirs.Length - offset); i++)
    {
        result += "..\\";
    }

    // step into the file path
    for (int i = offset; i < fileDirs.Length-1; i++)
    {
        result += fileDirs[i] + "\\";
    }

    // append the file
    result += fileDirs[fileDirs.Length - 1];

    return result;
}

This code is probably not bullet-proof but this is what I came up with. It's a little more robust. It takes two paths and returns path B as relative to path A.

example:

MakeRelativePath("c:\\dev\\foo\\bar", "c:\\dev\\junk\\readme.txt")
//returns: "..\\..\\junk\\readme.txt"

MakeRelativePath("c:\\dev\\foo\\bar", "c:\\dev\\foo\\bar\\docs\\readme.txt")
//returns: "docs\\readme.txt"