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

本文关键字:路径 方法 相对 URI 计算 | 更新日期: 2023-09-27 17:58:08

似乎没有标准的方法从相对URI(this property is valid only for absolute URIs)计算LocalPath,将其与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%稳定的。

有更好的方法吗

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

(注意:与其说是一个答案,不如说是一种哀叹,但我希望其中一些内容能提供信息。)

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

最初的问题是计算相对于某个根的路径-路径的倒数。Combine看起来和你的目标是一样的,在我对这段话应用EDIT之前,我建议看一看。但仔细观察,发现我写这篇文章时的解决方案并不是很好。

我对Uri的担忧在于,它是围绕URI路径的规则设计的,而这些规则不一定与文件系统路径的规则相同。例如,URI规范规定"."的路径段"用于相对路径引用的开头",而对于文件系统路径,将它们放在路径中间是完全合法的(尽管有点奇怪)。例如,c:'.'a'.'b'.'c是合法的,其含义与c:'a'b'c相同。

众所周知,文件系统规范化很容易出错,因此可能还有比这更微妙的问题。

因此,从理论上讲,特定于文件系统的API会比使用设计用于处理URI的代码更好,希望它能产生在文件系统上工作的结果。在实践中,.NET似乎没有提供用于计算相对路径的文件系统软件API,令人惊讶的是,用于此目的的Win32 API PathRelativePathTo将"."问题弄错了。。。

[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/

现在,对于斜线差异"''"与"/",如果您将最终结果包装在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'