从相对URI计算本地路径的正确方法

时间:2013-02-07 17:57:43

标签: c# vb.net winforms uri

似乎没有标准方法可以从相对LocalPaththis property is valid only for absolute URIs)计算URI,以便与Path.Combine结合使用,例如合并它带有文件掩码(* .ext)。问题是MakeRelativeUri会产生与my%20folder/类似的内容,而不是my folder\

以下是我找到的解决方法:

Module Module1
  Sub Main()
    Dim path1 As String = "C:\my folder\"
    Dim path2 As String = "C:\"
    MsgBox(GetPathDiff(path1, path2)) 'outputs "my folder\" (without quotes)
  End Sub

  Private Function GetPathDiff(path1 As String, path2 As String) As String
    Dim uri1 As New Uri(path1)
    Dim uri2 As New Uri(path2)
    Dim uri3 As Uri = uri2.MakeRelativeUri(uri1)
    Return Uri.UnescapeDataString(uri3.OriginalString).Replace("/", "\")
  End Function
End Module

我发现这是一种相当笨拙的方式,并且可能会有一些隐藏的石头我还没有偶然发现,即这种方法对于不同的使用情况并非100%稳定。

有更好的方法吗?

2 个答案:

答案 0 :(得分:2)

(注意:并不是一个悲伤的答案,但我希望其中一些是有用的。)

如果你在这里尝试做的是获得两个文件系统路径之间的相对路径,那么你最好坚持使用filesytem API。

最初Calculating the path relative to some root- the inverse of Path.Combine的问题看起来与你的目标相同,在我对此段落应用 EDIT 之前,我建议查看它。但仔细观察后,我发现这里的解决方案并不是那么好。

我对Uri的担忧是它围绕URI路径的规则设计,这些规则不一定与文件系统路径的规则相同。例如,URI规范表示路径段为“。”。 “打算在相对路径引用的开头使用”,而对于文件系统路径,将它们放在路径中间是完全合法的(如果有点奇怪)。例如。 c:\.\a\.\b\.\c是合法的,与c:\a\b\c相同。

文件系统规范化很容易出错,因此可能存在比这更微妙的问题。

因此从理论上讲,特定于文件系统的API比使用设计用于处理URI的代码更好,希望它能产生适用于文件系统的结果。在实践中,.NET似乎不提供用于计算相对路径的文件系统感知API,并且令人惊讶的是,出于此目的的PathRelativePathTo的Win32 API获得了“。”发错了......

答案 1 :(得分:1)

[editedit] 好吧,经过一番沉思,我做了想出了一个替代方案,但它可能并不适合所有人:

void Main()
{
    var path1 = @"C:\Program Files\Internet Explorer\";
    var path2 = @"C:\temp\";
    var sb = new StringBuilder(1000);
    PathRelativePathTo(sb, path1, 0, path2, 0);
    sb.ToString().Dump();
}

/*
BOOL PathRelativePathTo(
  _Out_  LPTSTR pszPath,
  _In_   LPCTSTR pszFrom,
  _In_   DWORD dwAttrFrom,
  _In_   LPCTSTR pszTo,
  _In_   DWORD dwAttrTo
);
*/
[DllImport("Shlwapi.dll")]
[return:MarshalAs(UnmanagedType.Bool)]
public static extern bool PathRelativePathTo(
    [Out] StringBuilder result,
    [In] string pathFrom,
    [In] int dwAttrFrom,
    [In] string pathTo,
    [In] int dwAttrTo);

哦,只是有个主意 - 这会让你(或多或少)得到你需要的东西吗?

public string PathDiff(string path1, string path2)
{
    var replace1 = path1.Replace(path2, string.Empty);
    var replace2 = path2.Replace(path1, string.Empty);
    return Path.IsPathRooted(replace1) ? replace2 : replace1;
}

或者可能更好:

public string PathDiff(string path1, string path2)
{
    return path1.Length > path2.Length ? 
        path1.Replace(path2, string.Empty) : 
        path2.Replace(path1, string.Empty);
}

(编辑:derp,命中提交过早):

不幸的是,没有内置的相对路径助手,但你基本上已经得到了你所拥有的东西,就像这样:

var path1 = @"C:\dev\src\release\Frontend\";
var path2 = @"C:\dev\src\";

var path1Uri = new Uri(path1);
var path2Uri = new Uri(path2);

var from1to2 = path1Uri.MakeRelativeUri(path2Uri).OriginalString;
var from2to1 = path2Uri.MakeRelativeUri(path1Uri).OriginalString;

Console.WriteLine("To go from {0} to {1}, you need to {2}", path1, path2, from1to2);
Console.WriteLine("To go from {0} to {1}, you need to {2}", path2, path1, from2to1);

输出:

To go from C:\dev\src\release\Frontend\ to C:\dev\src\, you need to ../../
To go from C:\dev\src\ to C:\dev\src\release\Frontend\, you need to release/Frontend/

现在,对于斜杠差异“\”vs“/”,如果将最终结果包装在Path.GetFullPath中,它将自动解决差异:

Console.WriteLine(Path.GetFullPath(Path.Combine(path1, from1to2)));
Console.WriteLine(Path.GetFullPath(Path.Combine(path2, from2to1)));

输出:

C:\dev\src\
C:\dev\src\release\Frontend\
相关问题