提问



Path.Combine很方便,但在.NET框架中是否有类似的URL功能?[56] [57]


我正在寻找这样的语法:


Url.Combine("http://MyUrl.com/", "/Images/Image.jpg")


会返回:


"http://MyUrl.com/Images/Image.jpg"

最佳参考


Uri有一个构造函数应该为你做这个:new Uri(Uri baseUri, string relativeUri) [58]


这是一个例子:


Uri baseUri = new Uri("http://www.contoso.com");
Uri myUri = new Uri(baseUri, "catalog/shownew.htm");

其它参考1


你用Uri.TryCreate( ... ):


Uri result = null;

if (Uri.TryCreate(new Uri("http://msdn.microsoft.com/en-us/library/"), "/en-us/library/system.uri.trycreate.aspx", out result))
{
    Console.WriteLine(result);
}


将返回:



  的 http://msdn.microsoft.com/en-us/library/system.uri.trycreate.aspx [59]


其它参考2


这可能是一个非常简单的解决方案:


public static string Combine(string uri1, string uri2)
{
    uri1 = uri1.TrimEnd('/');
    uri2 = uri2.TrimStart('/');
    return string.Format("{0}/{1}", uri1, uri2);
}

其它参考3


这里已经有了一些很好的答案。根据mdsharpe的建议,这里有一个扩展方法,可以在你想要处理Uri实例时轻松使用:


using System;
using System.Linq;

public static class UriExtensions
{
    public static Uri Append(this Uri uri, params string[] paths)
    {
        return new Uri(paths.Aggregate(uri.AbsoluteUri, (current, path) => string.Format("{0}/{1}", current.TrimEnd('/'), path.TrimStart('/'))));
    }
}


用法示例:


var url = new Uri("http://example.com/subpath/").Append("/part1/", "part2").AbsoluteUri;


这将产生http://example.com/subpath/part1/part2 [60]

其它参考4


这个问题得到了一些很好的,高度投票的答案!


Ryan Cook的答案接近我所追求的,可能更适合其他开发人员。但是,它将http://添加到字符串的开头,并且通常它比我之后的格式更多。


另外,对于我的用例,解析相对路径并不重要。


mdsharp的答案也包含一个好主意的种子,虽然实际的实现需要更多的细节才能完成。这是为了充实它(并且我在生产中使用它):


C#


public string UrlCombine(string url1, string url2)
{
    if (url1.Length == 0) {
        return url2;
    }

    if (url2.Length == 0) {
        return url1;
    }

    url1 = url1.TrimEnd('/', '\\');
    url2 = url2.TrimStart('/', '\\');

    return string.Format("{0}/{1}", url1, url2);
}


VB.Net


Public Function UrlCombine(ByVal url1 As String, ByVal url2 As String) As String
    If url1.Length = 0 Then
        Return url2
    End If

    If url2.Length = 0 Then
        Return url1
    End If

    url1 = url1.TrimEnd("/"c, "\"c)
    url2 = url2.TrimStart("/"c, "\"c)

    Return String.Format("{0}/{1}", url1, url2)
End Function


此代码通过以下测试,恰好在VB中:


<TestMethod()> Public Sub UrlCombineTest()
    Dim target As StringHelpers = New StringHelpers()

    Assert.IsTrue(target.UrlCombine("test1", "test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("test1/", "test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("test1", "/test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("test1/", "/test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("/test1/", "/test2/") = "/test1/test2/")
    Assert.IsTrue(target.UrlCombine("", "/test2/") = "/test2/")
    Assert.IsTrue(target.UrlCombine("/test1/", "") = "/test1/")
End Sub

其它参考5


根据您提供的示例网址,我将假设您要合并相对于您网站的网址。[61]


基于这个假设,我将建议这个解决方案作为对你的问题的最恰当的回答:Path.Combine很方便,框架中是否有 类似功能 对于网址?


由于URL框架中存在 类似函数 ,我建议正确的是:VirtualPathUtility.Combine方法。
这是MSDN参考链接:VirtualPathUtility.Combine方法[62]


有一点需要注意:我认为这仅适用于相对于您网站的网址(也就是说,您无法使用它来生成指向其他网站的链接。例如,var url = VirtualPathUtility.Combine("www.google.com", "accounts/widgets");)。

其它参考6


Path.Combine对我不起作用,因为可能有像|这样的字符在QueryString参数中,因此在Url中,这将导致ArgumentException。


我首先尝试了新的Uri(Uri baseUri,字符串relativeUri)方法,因为Uri像http://www.mediawiki.org/wiki/Special:SpecialPages那样对我失败了:


new Uri(new Uri("http://www.mediawiki.org/wiki/"), "Special:SpecialPages")


将导致Special:SpecialPages,因为Special之后的冒号表示一个方案。


所以我最终不得不采用mdsharpe/Brian MacKays路线并进一步开发它以使用多个uri部件:


public static string CombineUri(params string[] uriParts)
{
    string uri = string.Empty;
    if (uriParts != null && uriParts.Count() > 0)
    {
        char[] trims = new char[] { '\\', '/' };
        uri = (uriParts[0] ?? string.Empty).TrimEnd(trims);
        for (int i = 1; i < uriParts.Count(); i++)
        {
            uri = string.Format("{0}/{1}", uri.TrimEnd(trims), (uriParts[i] ?? string.Empty).TrimStart(trims));
        }
    }
    return uri;
}


用法:CombineUri("http://www.mediawiki.org/", "wiki", "Special:SpecialPages")

其它参考7


Path.Combine("Http://MyUrl.com/", "/Images/Image.jpg").Replace("\\", "/")

其它参考8


我只是把小扩展方法放在一起


public static string UriCombine (this string val, string append)
        {
            if (String.IsNullOrEmpty(val)) return append;
            if (String.IsNullOrEmpty(append)) return val;
            return val.TrimEnd('/') + "/" + append.TrimStart('/');
        }


可以像这样使用:


"www.example.com/".UriCombine("/images").UriCombine("first.jpeg");

其它参考9


很有趣的例子,Ryan,以这个功能的链接结束。做得好。


一个建议Brian:如果你将这段代码包装在一个函数中,你可能想要在TryCreate调用之前使用UriBuilder来包装基本URL。


否则,基本URL必须包含该方案(UriBuilder将采用http://)。只是一个想法:


public string CombineUrl(string baseUrl, string relativeUrl) {
    UriBuilder baseUri = new UriBuilder(baseUrl);
    Uri newUri;

    if (Uri.TryCreate(baseUri.Uri, relativeUrl, out newUri))
        return newUri.ToString();
    else
        throw new ArgumentException("Unable to combine specified url values");
}

其它参考10


组合Url的多个部分可能有点棘手。您可以使用2参数构造函数Uri(baseUri, relativeUri),也可以使用Uri.TryCreate()效用函数。无论哪种情况,你最终都可能返回错误的结果,因为这些方法不断截断第一个参数baseUri的相对部分,即从http://google.com/some/thinghttp://google.com之类的东西。


为了能够将多个部分组合成最终的URL,您可以复制以下两个函数:


    public static string Combine(params string[] parts)
    {
        if (parts == null || parts.Length == 0) return string.Empty;

        var urlBuilder = new StringBuilder();
        foreach (var part in parts)
        {
            var tempUrl = tryCreateRelativeOrAbsolute(part);
            urlBuilder.Append(tempUrl);
        }
        return VirtualPathUtility.RemoveTrailingSlash(urlBuilder.ToString());
    }

    private static string tryCreateRelativeOrAbsolute(string s)
    {
        System.Uri uri;
        System.Uri.TryCreate(s, UriKind.RelativeOrAbsolute, out uri);
        string tempUrl = VirtualPathUtility.AppendTrailingSlash(uri.ToString());
        return tempUrl;
    }


可以在https://uricombine.codeplex.com/SourceControl/latest#UriCombine/Uri.cs [63]找到用于演示用法的单元测试的完整代码。


我有单元测试来涵盖3种最常见的情况:


其它参考11


在上面的所有答案中,这个答案可能会丢失,但我发现UriBuilder对这类事情非常有效。


UriBuilder urlb = new UriBuilder("http", _serverAddress, _webPort, _filePath);
Uri url = urlb.Uri;
return url.AbsoluteUri;


有关更多构造函数和文档,请参阅UriBuilder类 - MSDN。[64]

其它参考12


我知道这已经得到了回答,但是将它们结合起来并确保它始终正确的简单方法是...


string.Format("{0}/{1}", Url1.Trim('/'), Url2);

其它参考13


这里是微软的(OfficeDev PnP)方法UrlUtility.Combine:[65]


    const char PATH_DELIMITER = '/';

    /// <summary>
    /// Combines a path and a relative path.
    /// </summary>
    /// <param name="path"></param>
    /// <param name="relative"></param>
    /// <returns></returns>
    public static string Combine(string path, string relative) 
    {
        if(relative == null)
            relative = String.Empty;

        if(path == null)
            path = String.Empty;

        if(relative.Length == 0 && path.Length == 0)
            return String.Empty;

        if(relative.Length == 0)
            return path;

        if(path.Length == 0)
            return relative;

        path = path.Replace('\\', PATH_DELIMITER);
        relative = relative.Replace('\\', PATH_DELIMITER);

        return path.TrimEnd(PATH_DELIMITER) + PATH_DELIMITER + relative.TrimStart(PATH_DELIMITER);
    }


资料来源:GitHub [66]

其它参考14


我的通用解决方案


public static string Combine(params string[] uriParts)
{
    string uri = string.Empty;
    if (uriParts != null && uriParts.Any())
    {
        char[] trims = new char[] { '\\', '/' };
        uri = (uriParts[0] ?? string.Empty).TrimEnd(trims);

        for (int i = 1; i < uriParts.Length; i++)
        {
            uri = string.Format("{0}/{1}", uri.TrimEnd(trims), (uriParts[i] ?? string.Empty).TrimStart(trims));
        }
    }

    return uri;
}

其它参考15


我知道我迟到了,但我创造了这个功能,让你的生活更轻松


    /// <summary>
    /// the ultimate Path combiner of all time
    /// </summary>
    /// <param name="IsURL">
    /// true - if the paths are internet urls,false - if the paths are local urls,this is very important as this will be used to decide which separator will be used
    /// </param>
    /// <param name="IsRelative">just adds the separator at the beginning</param>
    /// <param name="IsFixInternal">fix the paths from within (by removing duplicate separators and correcting the separators)</param>
    /// <param name="parts">the paths to combine</param>
    /// <returns>the combined path</returns>
    public static string PathCombine(bool IsURL , bool IsRelative , bool IsFixInternal , params string[] parts)
    {
        if (parts == null || parts.Length == 0) return string.Empty;
        char separator = IsURL ? '/' : '\\';

        if (parts.Length == 1 && IsFixInternal)
        {

            string validsingle;
            if (IsURL)
            {
                validsingle = parts[0].Replace('\\' , '/');
            }
            else
            {
                validsingle = parts[0].Replace('/' , '\\');
            }
            validsingle = validsingle.Trim(separator);
            return (IsRelative ? separator.ToString() : string.Empty) + validsingle;
        }

        string final = parts
            .Aggregate
            (
            (string first , string second) =>
            {
                string validfirst;
                string validsecond;
                if (IsURL)
                {
                    validfirst = first.Replace('\\' , '/');
                    validsecond = second.Replace('\\' , '/');
                }
                else
                {
                    validfirst = first.Replace('/' , '\\');
                    validsecond = second.Replace('/' , '\\');
                }
                var prefix = string.Empty;
                if (IsFixInternal)
                {
                    if (IsURL)
                    {
                        if (validfirst.Contains("://"))
                        {
                            var tofix = validfirst.Substring(validfirst.IndexOf("://") + 3);
                            prefix = validfirst.Replace(tofix , string.Empty).TrimStart(separator);

                            var tofixlist = tofix.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);

                            validfirst = separator + string.Join(separator.ToString() , tofixlist);
                        }
                        else
                        {
                            var firstlist = validfirst.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
                            validfirst = string.Join(separator.ToString() , firstlist);
                        }

                        var secondlist = validsecond.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
                        validsecond = string.Join(separator.ToString() , secondlist);
                    }
                    else
                    {
                        var firstlist = validfirst.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
                        var secondlist = validsecond.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);

                        validfirst = string.Join(separator.ToString() , firstlist);
                        validsecond = string.Join(separator.ToString() , secondlist);
                    }

                }
                return prefix + validfirst.Trim(separator) + separator + validsecond.Trim(separator);
            }
            );
        return (IsRelative ? separator.ToString() : string.Empty) + final;
    }


它适用于URL和普通路径


用法:


    //fixes internal paths
    Console.WriteLine(PathCombine(true , true , true , @"\/\/folder 1\/\/\/\\/\folder2\///folder3\\/" , @"/\somefile.ext\/\//\"));
    //result : /folder 1/folder2/folder3/somefile.ext


    //doesn't fix internal paths
    Console.WriteLine(PathCombine(true , true , false , @"\/\/folder 1\/\/\/\\/\folder2\///folder3\\/" , @"/\somefile.ext\/\//\"));
    //result : /folder 1//////////folder2////folder3/somefile.ext


    //don't worry about url prefixes when fixing internal paths
    Console.WriteLine(PathCombine(true , false , true , @"/\/\/https:/\/\/\lul.com\/\/\/\\/\folder2\///folder3\\/" , @"/\somefile.ext\/\//\"));
    //result : https://lul.com/folder2/folder3/somefile.ext


    Console.WriteLine(PathCombine(false , true , true , @"../../../\\..\...\./../somepath" , @"anotherpath"));
    //result : \..\..\..\..\...\.\..\somepath\anotherpath

其它参考16


我知道这个问题得到了很好的回答,但我觉得这很有用,因为它有以下特点



  • 抛出空格或空格

  • 更接近模仿System.Io.Path
  • 的静态类
  • 获取多个Url细分的<params参数



注意:可以更改类名称Url,因为系统类System.Security.Policy.Url





public static class Url
{
   private static string InternalCombine(string source, string dest)
   {

      // If the source is null or white space retune the dest only
      if (string.IsNullOrWhiteSpace(source))
      {
         throw new ArgumentException("Cannot be null or white space", "source");
         // throw new ArgumentException("Cannot be null or white space", nameof(source)); // c# 6.0 Nameof Expression
      }

      if (string.IsNullOrWhiteSpace(dest))
      {
         throw new ArgumentException("Cannot be null or white space", "dest");
         // throw new ArgumentException("Cannot be null or white space", nameof(dest)); // c# 6.0 Nameof Expression
      }

      source =  source.TrimEnd('/', '\\');
      dest = dest.TrimStart('/', '\\');

      return string.Format("{0}/{1}", source, dest);
      // return $"{source}/{dest}"; // c# 6.0 string interpolation
   }

   public static string Combine(string source, params string[] args)
   {
      return args.Aggregate(source, InternalCombine);
   }
}


结果


Url.Combine("test1", "test2");    
Url.Combine("test1//", "test2"); 
Url.Combine("test1", "/test2");

// Result = test1/test2

Url.Combine(@"test1\/\/\/", @"\/\/\\\\\//test2", @"\/\/\\\\\//test3\") ;

// Result = test1/test2/test3

Url.Combine("/test1/", "/test2/", null);
Url.Combine("", "/test2/");
Url.Combine("/test1/", null);

// Throws an ArgumentException

其它参考17


这个怎么样?


 public static class WebPath
    {
        public static string Combine(params string[] args)
        {
            var prefixAdjusted = args.Select(x => x.StartsWith("/") && !x.StartsWith("http") ? x.Substring(1) : x);
            return string.Join("/", prefixAdjusted);
        }
    }

其它参考18


这是我的方法,我也将自己使用它


public static string UrlCombine(string part1, string part2)
{
    string newPart1 = string.Empty;
    string newPart2 = string.Empty;
    string seprator = "/";

    // if either part1 or part 2 is empty,
    // we don't need to combine with seprator
    if (string.IsNullOrEmpty(part1) || string.IsNullOrEmpty(part2))
    {
        seprator = string.Empty;
    }

    // if part1 is not empty
    // remove '/' at last
    if (!string.IsNullOrEmpty(part1))
    {
        newPart1 = part1.TrimEnd('/');
    }

    // if part2 is not empty
    // remove '/' at first
    if (!string.IsNullOrEmpty(part2))
    {
        newPart2 = part2.TrimStart('/');
    }

    // now finally combine
    return string.Format("{0}{1}{2}", newPart1, seprator, newPart2);
}

其它参考19


    private Uri UriCombine(string path1, string path2, string path3 = "", string path4 = "")
    {
        string path = System.IO.Path.Combine(path1, path2.TrimStart('\\', '/'), path3.TrimStart('\\', '/'), path4.TrimStart('\\', '/'));
        string url = path.Replace('\\','/');
        return new Uri(url);
    }


行为的好处与Path.Combine完全一样

其它参考20


将URL与URI 组合在一起的规则


为了避免奇怪的行为,有一条规则要遵循:



  • path(目录)必须以/结尾。如果路径没有/结束,则最后一部分被视为文件名,并且在尝试与下一个URL部分组合时将被连接。

  • 有1个例外:基本URL地址(没有目录信息)不需要以/
  • 结尾
  • 路径部分不能以/开头,如果以/开头,则删除URL中的每个现有相关信息...添加string.Empty部分路径也将从URL中删除相关目录!



如果您遵循上述规则,则可以将网址与下面的代码结合使用。根据您的具体情况,您可以向网址添加多个目录部分...


        var pathParts = new string[] { destinationBaseUrl, destinationFolderUrl, fileName };

        var destination = pathParts.Aggregate((left, right) =>
        {
            if (string.IsNullOrWhiteSpace(right))
                return left;

            return new Uri(new Uri(left), right).ToString();
        });

其它参考21


为什么不使用以下内容。


System.IO.Path.Combine(rootUrl, subPath).Replace(@"\", "/")

其它参考22


更多建议......
我综合了以上所有内容:


    public static string UrlPathCombine(string path1, string path2)
    {
        path1 = path1.TrimEnd('/') + "/";
        path2 = path2.TrimStart('/');

        return Path.Combine(path1, path2)
            .Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
    }

    [TestMethod]
    public void TestUrl()
    {
        const string P1 = "http://msdn.microsoft.com/slash/library//";
        Assert.AreEqual("http://msdn.microsoft.com/slash/library/site.aspx", UrlPathCombine(P1, "//site.aspx"));

        var path = UrlPathCombine("Http://MyUrl.com/", "Images/Image.jpg");

        Assert.AreEqual(
            "Http://MyUrl.com/Images/Image.jpg",
            path);
    }

其它参考23


好吧,我只是连接两个字符串并使用正则表达式来执行清理部分。


    public class UriTool
    {
        public static Uri Join(string path1, string path2)
        {
            string url = path1 + "/" + path2;
            url = Regex.Replace(url, "(?<!http:)/{2,}", "/");

            return new Uri(url);
        }
    }


所以,你可以像这样使用:


    string path1 = "http://someaddress.com/something/";
    string path2 = "/another/address.html";
    Uri joinedUri = UriTool.Join(path1, path2);

    // joinedUri.ToString() returns "http://someaddress.com/something/another/address.html"


希望它对某人有用!

其它参考24


我使用此代码来解决问题:


string[] brokenBaseUrl = Context.Url.TrimEnd('/').Split('/');
string[] brokenRootFolderPath = RootFolderPath.Split('/');

for (int x = 0; x < brokenRootFolderPath.Length; x++)
{
    //if url doesn't already contain member, append it to the end of the string with / in front
    if (!brokenBaseUrl.Contains(brokenRootFolderPath[x]))
    {
        if (x == 0)
        {
            RootLocationUrl = Context.Url.TrimEnd('/');
        }
        else
        {
            RootLocationUrl += String.Format("/{0}", brokenRootFolderPath[x]);
        }
    }
}

其它参考25


这两项都有效


  Uri final = new Uri(Regex.Replace(baseUrl + "/" + relativePath, "(?<!http:)/{2,}", "/"));


要么


  Uri final =new Uri(string.Format("{0}/{1}", baseUrl.ToString().TrimEnd('/'), relativePath.ToString().TrimStart('/')));





如果


baseUrl =http://tesrurl.test.com/Int18'[67]





relativePath =To_Folder


输出= http://tesrurl.test.com/Int18/To_Folder [68]


下面的代码会出现一些错误


 // if you use below code, some issues will be there in final uri
 Uri final= new Uri(baseUrl ,relativePath );

其它参考26


简单的一个班轮:


public static string Combine(this string uri1, string uri2) => $"{uri1.TrimEnd('/')}/{uri2.TrimStart('/')}";


灵感来自@Matt Sharpe的回答。

其它参考27


我们使用以下简单的帮助方法将任意数量的URL部分连接在一起:


public static string JoinUrlParts(params string[] urlParts)
{
    return string.Join("/", urlParts.Where(up => !string.IsNullOrEmpty(up)).ToList().Select(up => up.Trim('/')).ToArray());
}


注意,它不支持../../something/page.htm'-相对URL-s!

其它参考28


上面有一个Todd Menier的评论,Flurl包含了一个Url.Combine。 [70]


更多细节:



  Url.Combine基本上是URL的Path.Combine,确保一个
  部件之间只有一个分隔符:



var url = Url.Combine(
    "http://foo.com/",
    "/too/", "/many/", "/slashes/",
    "too", "few?",
    "x=1", "y=2"
// result: "http://www.foo.com/too/many/slashes/too/few?x=1&y=2" 

其它参考29


我必须指出,Path.Combine似乎也可以直接在.NET4上运行