135

I'm trying to join a Windows path with a relative path using Path.Combine.

However, Path.Combine(@"C:\blah",@"..\bling") returns C:\blah\..\bling instead of C:\bling\.

Does anyone know how to accomplish this without writing my own relative path resolver (which shouldn't be too hard)?

4
  • 7
    We're getting different answers here.. I don't think it's a duplicate Commented Mar 22, 2009 at 5:23
  • 1
    it's duplicate, although i think Path.GetFullName is a better solution. Commented Mar 22, 2009 at 7:17
  • You just contradicted yourself. But thanks for the alternate answer. Commented Mar 22, 2009 at 7:52
  • possible duplicate of Path.Combine and the dot notation Commented May 30, 2014 at 18:53

8 Answers 8

94

What Works:

string relativePath = "..\\bling.txt";
string baseDirectory = "C:\\blah\\";
string absolutePath = Path.GetFullPath(baseDirectory + relativePath);

(result: absolutePath="C:\bling.txt")

What doesn't work

string relativePath = "..\\bling.txt";
Uri baseAbsoluteUri = new Uri("C:\\blah\\");
string absolutePath = new Uri(baseAbsoluteUri, relativePath).AbsolutePath;

(result: absolutePath="C:/blah/bling.txt")

Sign up to request clarification or add additional context in comments.

4 Comments

Yes, that is what I am insinuating with the post
Just make sure baseDirectory has the trailing \\, otherwise you end up with C:\\blah..\\bling.txt and that doesn't work. In that case you can manually add them to the string or do Path.GetFullPath(Path.Combine(baseDirectory, relativePath))
Shouldn't the result of your What Works section be C:\bling.txt?
Why does the URI-based method not work? According to this answer, the result is valid (and it seems to be recognized on Windows, as well).
56

Call Path.GetFullPath on the combined path http://msdn.microsoft.com/en-us/library/system.io.path.getfullpath.aspx

> Path.GetFullPath(Path.Combine(@"C:\blah\",@"..\bling"))
C:\bling

(I agree Path.Combine ought to do this by itself)

1 Comment

Note that this only works if the first path is an absolute path. It doesn't work for Path.GetFullPath(Path.Combine(@"..\..\blah",@"\bling"))
20

Path.GetFullPath(@"c:\windows\temp\..\system32")?

1 Comment

Note: should result in c:\windows\system32
6

Path.GetFullPath() does not work with relative paths.

Here's the solution that works with both relative + absolute paths. It works on both Linux + Windows and it keeps the .. as expected in the beginning of the text (at rest they will be normalized). The solution still relies on Path.GetFullPath to do the fix with a small workaround.

It's an extension method so use it like text.Canonicalize()

/// <summary>
///     Fixes "../.." etc
/// </summary>
public static string Canonicalize(this string path)
{
    if (path.IsAbsolutePath())
        return Path.GetFullPath(path);
    var fakeRoot = Environment.CurrentDirectory; // Gives us a cross platform full path
    var combined = Path.Combine(fakeRoot, path);
    combined = Path.GetFullPath(combined);
    return combined.RelativeTo(fakeRoot);
}
private static bool IsAbsolutePath(this string path)
{
    if (path == null) throw new ArgumentNullException(nameof(path));
    return
        Path.IsPathRooted(path)
        && !Path.GetPathRoot(path).Equals(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)
        && !Path.GetPathRoot(path).Equals(Path.AltDirectorySeparatorChar.ToString(), StringComparison.Ordinal);
}
private static string RelativeTo(this string filespec, string folder)
{
    var pathUri = new Uri(filespec);
    // Folders must end in a slash
    if (!folder.EndsWith(Path.DirectorySeparatorChar.ToString())) folder += Path.DirectorySeparatorChar;
    var folderUri = new Uri(folder);
    return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString()
        .Replace('/', Path.DirectorySeparatorChar));
}

Comments

5

For windows universal apps Path.GetFullPath() is not available, you can use the System.Uri class instead:

 Uri uri = new Uri(Path.Combine(@"C:\blah\",@"..\bling"));
 Console.WriteLine(uri.LocalPath);

Comments

4

This will give you exactly what you need (path does NOT have to exist for this to work)

DirectoryInfo di = new DirectoryInfo(@"C:\blah\..\bling");
string cleanPath = di.FullName;

1 Comment

Both Path.GetFullPath() and DirectoryInfo.FullName will work on a fictitious path. The problem is when the file actually exists, the executing process needs FileIOPermission - true for both APIs. (see MSDN)
1

Be careful with Backslashes, don't forget them (neither use twice:)

string relativePath = "..\\bling.txt";
string baseDirectory = "C:\\blah\\";
//OR:
//string relativePath = "\\..\\bling.txt";
//string baseDirectory = "C:\\blah";
//THEN
string absolutePath = Path.GetFullPath(baseDirectory + relativePath);

1 Comment

There's no problem with any of the answers nor the original question, as they use the "@", we don't have to double backslashes
1

To handle any absolute, relative, or URI base paths, it doesn't seem like there's one canned solution that fits all... so, I wrote one:

    public static String CombinePaths(String basepath, String relpath)
    {
        Stack<String> vs = new Stack<String>();

        int i;

        var s = basepath.Split('\\');
        for (i = 0; i < s.Length; i++)
        {
            if (s[i] != "..")
            {
                if (s[i] != ".")
                    vs.Push(s[i]);
            }
            else
            {
                vs.Pop();
            }
        }

        var r = relpath.Split('\\');
        for (i = 0; i < r.Length; i++)
        {
            if (r[i] != "..")
            {
                if (r[i] != ".")
                    vs.Push(r[i]);
            }
            else
            {
                vs.Pop();
            }
        }

        String ret = "";
        var a = vs.ToArray();
        i = a.Count() - 1;
        while (i > 0)
        {
            ret += a[i].ToString();
            ret += "\\";
            i--;
        }
        ret += a[0].ToString();

        return ret;
    }

Feel free to improve performance or apply whatever shortcuts apply to my answer (I don't usually work in C#; I usually write C++).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.