Is there a reliable way to calculate the inverse of Path.Combine()?
Path.Combine("c:\folder", "subdirectory\something.txt") might return something like "c:\folder\subdirectory\something.text". What I want is the inverse, a function where Path.GetRelativeUrl("c:\folder", "c:\folder\subdirectory\something.text") would return something like ""subdirectory\something.txt".
One solution is to do string comparisons and trim the roots, but this would not work when the same path is expressed in different ways (use of ".." or "~1" in path expression).
Combines two strings into a path. Combine(String, String, String) Combines three strings into a path. Combine(String, String, String, String) Combines four strings into a path.
Relative paths make use of two special symbols, a dot (.) and a double-dot (..), which translate into the current directory and the parent directory. Double dots are used for moving up in the hierarchy.
Alright so in my case I don't have some of the tougher edge cases (fullPath and relativePath mixing network map locations, super long filenames). What I ended up doing was creating the class below
public class PathUtil { static public string NormalizeFilepath(string filepath) { string result = System.IO.Path.GetFullPath(filepath).ToLowerInvariant(); result = result.TrimEnd(new [] { '\\' }); return result; } public static string GetRelativePath(string rootPath, string fullPath) { rootPath = NormalizeFilepath(rootPath); fullPath = NormalizeFilepath(fullPath); if (!fullPath.StartsWith(rootPath)) throw new Exception("Could not find rootPath in fullPath when calculating relative path."); return "." + fullPath.Substring(rootPath.Length); } }
It seems to work pretty well. At least, it passes these NUnit tests:
[TestFixture] public class PathUtilTest { [Test] public void TestDifferencesInCapitolizationDontMatter() { string format1 = PathUtil.NormalizeFilepath("c:\\windows\\system32"); string format2 = PathUtil.NormalizeFilepath("c:\\WindowS\\System32"); Assert.AreEqual(format1, format2); } [Test] public void TestDifferencesDueToBackstepsDontMatter() { string format1 = PathUtil.NormalizeFilepath("c:\\windows\\system32"); string format2 = PathUtil.NormalizeFilepath("c:\\Program Files\\..\\Windows\\System32"); Assert.AreEqual(format1, format2); } [Test] public void TestDifferencesInFinalSlashDontMatter() { string format1 = PathUtil.NormalizeFilepath("c:\\windows\\system32"); string format2 = PathUtil.NormalizeFilepath("c:\\windows\\system32\\"); Console.WriteLine(format1); Console.WriteLine(format2); Assert.AreEqual(format1, format2); } [Test] public void TestCanCalculateRelativePath() { string rootPath = "c:\\windows"; string fullPath = "c:\\windows\\system32\\wininet.dll"; string expectedResult = ".\\system32\\wininet.dll"; string result = PathUtil.GetRelativePath(rootPath, fullPath); Assert.AreEqual(expectedResult, result); } [Test] public void TestThrowsExceptionIfRootDoesntMatchFullPath() { string rootPath = "c:\\windows"; string fullPath = "c:\\program files\\Internet Explorer\\iexplore.exe"; try { PathUtil.GetRelativePath(rootPath, fullPath); } catch (Exception) { return; } Assert.Fail("Exception expected"); } }
The test cases rely on certain files existing.. these files are common on most Windows installs but your mileage may vary.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With