Path.Combine 用于 URL?

Path.Combine for URLs?

提问人:Brian MacKay 提问时间:12/17/2008 最后编辑:Peter MortensenBrian MacKay 更新时间:6/28/2022 访问量:474129

问:

Path.Combine 很方便,但是 .NET Framework 中是否有类似的 URL 函数?

我正在寻找这样的语法:

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

这将返回:

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

C# .NET asp.net URL 路径

评论

18赞 Todd Menier 2/21/2014
Flurl 包含一个可以做到这一点的方法。Url.Combine
2赞 Dave Gordon 7/7/2014
实际上,// 是由网站或服务器的路由处理的,而不是由浏览器处理的。它将发送您放入地址栏的内容。这就是为什么当我们键入 http:// 而不是 http:// 时会遇到问题 因此,// 可能会在某些网站上引起重大问题。我正在为一个爬虫编写一个 .dll,该爬虫处理一个特定的网站,如果您在 url 中有 // 则抛出 404。
0赞 JHBonarius 1/23/2023
请注意:请记住对输入字符串进行正确的 URL 编码(例如,使用 WebUtility.UrlEncode)。我发现 的构造函数没有为您执行此操作,导致 URL 无效!!Uri

答:

157赞 Ryan Cook 12/17/2008 #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

评论

62赞 Brian MacKay 10/29/2009
+1:这很好,尽管我对输出参数有一个不合理的问题。;)
12赞 Abel 8/27/2010
@Brian:如果有帮助,所有 TryXXX 方法 (, ) 都有此输出参数,以便更轻松地在 if 语句中使用它们。顺便说一句,您不必像 Ryan 在此示例中那样初始化变量。int.TryParseDateTime.TryParseExact
51赞 Matt Kocaj 8/27/2013
这个答案遇到了与乔尔相同的问题:加入并将导致这似乎不是你想要的。test.com/mydirectory//helloworld.aspxtest.com/helloworld.aspx
3赞 Faisal Mq 11/28/2013
嗨,以下原因失败: if (Uri.TryCreate(new Uri(“localhost/MyService/”), “/Event/SomeMethod?abc=123”, out result)) { Console.WriteLine(result);它向我显示结果为:localhost/Event/SomeMethod?abc=123 注意:“http://”从此处的基本 Uri 替换为 stackoverflow
4赞 Tom Lint 1/24/2014
@FaisalMq 这是正确的行为,因为您传递了相对于根的第二个参数。如果你在第二个参数上省略了前导 /,你就会得到你期望的结果。
13赞 mtazva 7/30/2009 #2

诙谐的例子,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");
}
224赞 Matthew Sharpe 9/25/2009 #3

这可能是一个适当的简单解决方案:

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

评论

10赞 Brian MacKay 5/8/2010
+1:虽然这不处理相对样式的路径(../../whatever.html),我喜欢这个,因为它很简单。我还会为“\”字符添加修剪。
3赞 Brian MacKay 5/12/2010
请参阅我的答案,以获得更完整的版本。
0赞 Mladen B. 3/22/2021
@BrianMacKay,OP从不要求相对式的路径......
2赞 Brian MacKay 3/23/2021
@MladenB。好吧,我是OP。 :)虽然我没有明确要求,但支持相对式路径的需求是总体问题域的固有部分......如果不这样做,如果人们试图重复使用它,可能会导致令人困惑的结果。
1赞 4/8/2022
2022年:虽然这是一个不错的解决方案,但可能不建议以同样的方式用于 URL 和文件夹路径(您将使用它们)stringPath.xxx()
1330赞 Joel Beckham 10/7/2009 #4

Uri 有一个构造函数,应该为你执行此操作:new Uri(Uri baseUri, string relativeUri)

下面是一个示例:

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

编者注:请注意,此方法无法按预期工作。在某些情况下,它可以剪切部分 baseUri。请参阅评论和其他答案。

评论

440赞 Doctor Jones 10/28/2010
我喜欢使用 Uri 类,不幸的是,它不会像 OP 要求的那样表现为 Path.Combinbin。例如,new Uri(new Uri(“test.com/mydirectory/”), “/helloworld.aspx”)。ToString() 给你 “test.com/helloworld.aspx”;如果我们想要一个 Path.Combine 样式的结果,这将是不正确的。
235赞 Joel Beckham 10/29/2010
这一切都在斜杠中。如果相对路径部分以斜杠开头,则其行为与您描述的一样。但是,如果你省略了斜杠,那么它就会按照你预期的方式工作(注意第二个参数上缺少的斜杠):new Uri(new Uri(“test.com/mydirectory/”), “helloworld.aspx”)。ToString() 的结果为“test.com/mydirectory/helloworld.aspx”。Path.Combine 的行为与此类似。如果相对路径参数以斜杠开头,则它仅返回相对路径,而不将它们组合在一起。
86赞 Carl 1/13/2011
如果 baseUri 恰好是“test.com/mydirectory/mysubdirectory”,则结果将是“test.com/mydirectory/helloworld.aspx”而不是“test.com/mydirectory/mysubdirectory/helloworld.aspx”。细微的区别在于第一个参数上没有尾部斜杠。我完全赞成使用现有的框架方法,如果我必须已经有尾部斜杠,那么我认为做 partUrl1 + partUrl2 的味道要少得多 - 我可能已经追逐了那个尾部斜杠很长一段时间,所有这些都是为了不做字符串连接。
79赞 nickd 3/26/2011
我想要 URI 组合方法的唯一原因是这样我就不必检查尾部斜杠。如果应用程序位于根目录,则 Request.ApplicationPath 为“/”,但如果不是,则为“/foo”。
35赞 Baptiste Pernet 4/21/2011
我 -1 这个答案,因为这不能回答问题。当你想合并 url 时,比如当你想使用 Path.Combine 时,你不想关心尾随的 /。有了这个,你必须关心。我更喜欢上面的 Brian MacKay 或 mdsharpe 的解决方案
35赞 Jeronimo Colon III 3/28/2010 #5

根据您提供的示例 URL,我假设您要合并与您的网站相关的 URL

基于这个假设,我将提出这个解决方案作为对你的问题的最合适的回答,即:“Path.Combinable很方便,URL框架中是否有类似的功能

由于 URL 框架中有一个类似的函数,我建议正确的是:“VirtualPathUtility.Combine”方法。 下面是 MSDN 参考链接:VirtualPathUtility.Combine 方法

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

评论

0赞 Brian MacKay 3/30/2010
+1,因为它接近我正在寻找的东西,尽管如果它适用于任何旧 url,那将是理想的。我把它加倍会变得比 mdsharpe 提议的要优雅得多。
2赞 Abel 8/27/2010
警告是正确的,它不能与绝对 uri 一起使用,并且结果始终是相对于根的。但它还有一个额外的好处,它可以处理波浪号,就像“~/”一样。这使它成为和组合的捷径。Server.MapPath
111赞 Brian MacKay 5/11/2010 #6

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

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

MDSwitch 的回答也包含了一个好主意的种子,尽管实际实现需要更多细节才能完成。这是充实它的尝试(我正在生产中使用它):

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

评论

4赞 Abel 8/27/2010
谈到细节:如果论点是强制性的呢?对不起,只是挑剔;-)。请注意,反斜杠与 URI 无关(如果存在,则不应修剪),因此可以从 TrimXXX 中删除它。ArgumentNullException("url1")Nothing
7赞 Jaider 6/13/2012
您可以使用参数 string[] 并递归地连接它们以允许 2 个以上的组合
9赞 Uriah Blatherwick 8/19/2014
我当然希望这是在像 Path.Combine 这样的基类库中。
1赞 JJS 7/8/2016
@MarkHurd我再次编辑了代码,使其在行为上与 C# 相同,并且在语法上也等效。
1赞 JJS 7/11/2016
@BrianMacKay我打破了它,Markhurd 指出了我的错误并回滚,我再次更新......干杯
16赞 Alex 5/13/2010 #7

将它们组合起来并确保它始终正确的一种简单方法是:

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

评论

0赞 Brian MacKay 5/13/2010
+1,尽管这与 mdsharpe 的答案非常相似,我在回答中对其进行了改进。除非 Url2 以 / 或 \ 开头,或者 Url1 意外以 \ 结尾,或者其中任何一个为空,否则此版本效果很好!:)
18赞 urza 11/25/2010 #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");

评论

0赞 controlbox 9/29/2023
我个人避免在字符串和 int 等核心类型上使用扩展方法,因为您最终会得到大量它们,并且每次您想在任何地方使用与 99% 代码无关的字符串时,它们都会作为建议弹出。
27赞 JeremyWeir 1/12/2011 #9
Path.Combine("Http://MyUrl.com/", "/Images/Image.jpg").Replace("\\", "/")

评论

15赞 Jaider 6/13/2012
path.Replace(Path.DirectorySeparatorChar, '/');
6赞 SliverNinja - MSFT 8/16/2012
path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
1赞 Per G 4/25/2013
要让它 wrk,您必须删除第二个参数中的第一个 / 即 “/Images” - / Path.Combine(“Http://MyUrl.com”, “Images/Image.jpg”)
11赞 user247702 3/13/2014
@SliverNinja 这是不正确的 此字段的值在 UNIX 上是反斜杠 ('\'),在 Windows 和 Macintosh 操作系统上是斜杠 ('/')。在 Linux 系统上使用 Mono 时,你会得到错误的分隔符。
7赞 JeremyWeir 7/27/2016
所有在目录分隔符上徘徊的人都忘记了字符串可能来自与您现在不同的操作系统。只需将反斜杠替换为正斜杠,即可获得保障。
49赞 Mike Fuchs 7/15/2011 #10

Path.Combine 对我不起作用,因为 QueryString 参数中可能存在类似“|”的字符,因此 URL 中可能存在,这将导致 ArgumentException。

我首先尝试了新方法,但由于 URI 如下,它对我来说失败了:Uri(Uri baseUri, string relativeUri)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.Length > 0)
    {
        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;
}

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

评论

3赞 Brian MacKay 7/18/2011
+1: 现在我们来谈谈......我要试试这个。这甚至可能最终成为新的公认的答案。在尝试新的 Uri() 方法后,我真的不喜欢它。太狡猾了。
0赞 Gromer 8/7/2012
这正是我需要的!不喜欢在乎我把尾部斜杠放在哪里,等等......
0赞 NightOwl888 3/6/2014
+1 用于滚动 null 检查,因此它不会爆炸。
0赞 PRMan 6/6/2019
Count() 应为 Length,这样您就不需要为此在库中包含 Linq。
1赞 Slate 6/24/2021
注意将从协议中截断一个正斜杠,如果你指定它与主机分开,例如(“https://”,主机,端点)
184赞 Ales Potocnik Hahonina 11/3/2011 #11

这里已经有一些很好的答案。根据 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

如果你想使用字符串而不是 Uris,那么以下内容也会产生相同的结果,只需调整它以满足您的需求:

public string JoinUriSegments(string uri, params string[] segments)
{
    if (string.IsNullOrWhiteSpace(uri))
        return null;

    if (segments == null || segments.Length == 0)
        return uri;

    return segments.Aggregate(uri, (current, segment) => $"{current.TrimEnd('/')}/{segment.TrimStart('/')}");
}

var uri = JoinUriSegements("http://example.com/subpath/", "/part1/", "part2");

评论

5赞 angularsen 11/19/2011
此解决方案使编写与 Path.Combine() 非常相似的 UriUtils.Combine(“base url”, “part1”, “part2”, ...) 静态方法变得非常简单。好!
0赞 angularsen 11/21/2011
为了支持相对 URI,我不得不在 Uri 构造函数中使用 ToString() 而不是 AbsoluteUri 和 UriKind.AbsoluteOrRelative。
0赞 Ales Potocnik Hahonina 11/21/2011
感谢您提供有关相对 Uris 的提示。不幸的是,Uri 并不容易处理相对路径,因为涉及 Request.ApplicationPath 总是有一些麻烦。也许您也可以尝试使用新的 Uri(HttpContext.Current.Request.ApplicationPath) 作为基础,并在其上调用 Append?这将为您提供绝对路径,但应该在站点结构中的任何位置工作。
0赞 n.podbielski 1/15/2016
我还添加了检查要追加的任何路径是否为空或空字符串。
0赞 Arvo Bowen 2/6/2021
当我看着所有的答案时,我想......“为什么还没有人发布扩展方法,我要发布一个”......没关系。+1
2赞 Martin Murphy 12/10/2012 #12

使用这个:

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);
    }
}

评论

0赞 Brian MacKay 12/11/2012
与“WebPath”的接触很好。:)不过,代码可能过于密集 - 我很难瞥一眼并说,是的,这很完美。这让我想看单元测试。也许那只是我!
1赞 penguat 12/13/2012
x.StartsWith(“/”) && !x.StartsWith(“http”) - 为什么要进行 http 检查?你会得到什么?
0赞 Martin Murphy 4/17/2013
如果斜杠以 http 开头,您不想尝试剥离斜杠。
0赞 Martin Murphy 4/17/2013
@BrianMacKay,我不确定两班轮是否值得进行单元测试,但如果您愿意,请随时提供一个。这并不是说我接受补丁或任何东西,但请随意编辑建议。
-2赞 missbassethorn 2/28/2013 #13

我还没有使用过以下代码,但在我的互联网旅行中发现了它来解决 URL 组合问题 - 希望这是一个简洁(并且成功!

VirtualPathUtility.Combine

评论

3赞 Mark Hurd 3/2/2013
真的不太有用。有许多谷歌点击解释了它的一些问题,但是,除了不喜欢“http://......”在开始时,如果它不以“/”结尾,它实际上会删除第一个参数的最后一个子路径!不过,MSDN的描述听起来不错!
0赞 Believe2014 5/1/2014
我已经在我的回答中解释并提供了这个问题的解决方案 stackoverflow.com/a/23399048/3481183
1赞 Per G 4/25/2013 #14

我综合了之前的所有答案:

    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);
    }

评论

1赞 Believe2014 5/1/2014
您可以使用 VirtualPathUtiliy 类安全地追加和删除尾部斜杠。看看我的答案: stackoverflow.com/a/23399048/3481183
10赞 javajavajavajavajava 5/22/2013 #15

我发现对于这种事情非常有效:UriBuilder

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

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

3赞 Amit Bhagat 8/3/2013 #16

这是我的方法,我也会用到它:

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

    // If either part1 or part 2 is empty,
    // we don't need to combine with seperator
    if (string.IsNullOrEmpty(part1) || string.IsNullOrEmpty(part2))
    {
        seperator = 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, seperator, newPart2);
}

评论

0赞 Believe2014 5/1/2014
这仅适用于您的情况。有些情况可能会破坏您的代码。此外,您没有对路径的各个部分进行正确的编码。当涉及到跨站点脚本攻击时,这可能是一个巨大的漏洞。
0赞 Amit Bhagat 5/2/2014
我同意你的观点。该代码应该只是简单地组合两个 url 部分。
1赞 TruthOf42 2/18/2014 #17

用:

    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

10赞 Believe2014 5/1/2014 #18

合并 URL 的多个部分可能有点棘手。您可以使用双参数构造函数,也可以使用实用程序函数。Uri(baseUri, relativeUri)Uri.TryCreate()

无论哪种情况,您最终都可能返回不正确的结果,因为这些方法会不断截断第一个参数的相对部分,即从 to .baseUrihttp://google.com/some/thinghttp://google.com

为了能够将多个部分合并到一个最终到达网址中,您可以复制以下两个函数:

    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 中找到

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

Enter image description here

评论

1赞 Brian MacKay 5/4/2014
+1 表示所有额外的努力。对于一些得票较高的答案,我需要稍微维护一下这个问题,你已经放弃了挑战。;)
0赞 Marcio Martins 5/16/2014 #19

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

    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"
4赞 Alex Titarenko 5/17/2015 #20

我的通用解决方案:

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;
}

评论

0赞 Shiva 5/22/2017
此帮助程序方法非常灵活,在许多不同的用例中都运行良好。谢谢!
4赞 user3638471 11/2/2015 #21

下面是 Microsoft 的 (OfficeDev PnP) 方法 UrlUtility.Combine

    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

评论

0赞 Brian MacKay 11/3/2015
看起来这可能是针对路径的,而不是 URL。
0赞 11/4/2015
@BrianMacKay 同意它看起来像它,但它来自 UrlUtility 类,并在组合 URL 的上下文中使用
2赞 11/4/2015
经过编辑以阐明它属于哪个类
0赞 Harry Berry 9/26/2017
使用此类时要小心,该类的其余部分包含特定于 SharePoint 的项目。
0赞 Joshua Smith 12/4/2015 #22

我用这段代码来解决这个问题:

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]);
        }
    }
}
2赞 baHI 4/5/2016 #23

将 URL 与 URI 组合时的规则

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

  • 路径(目录)必须以“/”结尾。如果路径以“/”结尾,则最后一部分将被视为文件名,并且在尝试与下一个 URL 部分合并时将连接起来。
  • 有一个例外:基本 URL 地址(不含目录信息)不需要以“/”结尾
  • 路径部分不得以“/”开头。如果它以“/”开头,则 URL 中所有现有的相对信息都将被删除......添加部件路径也会从 URL 中删除相对目录!string.Empty

如果您遵循上述规则,则可以将 URL 与以下代码组合在一起。根据您的情况,您可以在 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();
        });
3赞 Ahmed Fwela 7/22/2016 #24

我创建了这个功能,可以让你的生活更轻松:

    /// <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
5赞 TheGeneral 2/28/2017 #25

我发现以下内容很有用,并具有以下功能:

  • 抛出 null 或空格
  • 为多个 Url 段采用多个参数params
  • 抛出 null 或空

public static class UrlPath
{
   private static string InternalCombine(string source, string dest)
   {
      if (string.IsNullOrWhiteSpace(source))
         throw new ArgumentException("Cannot be null or white space", nameof(source));

      if (string.IsNullOrWhiteSpace(dest))
         throw new ArgumentException("Cannot be null or white space", nameof(dest));

      return $"{source.TrimEnd('/', '\\')}/{dest.TrimStart('/', '\\')}";
   }

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

测试

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

// Result = test1/test2

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

// Result = test1/test2/test3

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

// Throws an ArgumentException

评论

0赞 Moriya 1/22/2020
测试的一些问题: // 结果 = test1/test2/test3\ 对于第 4 个测试,最后一个抛出测试给出 ArgumentNullException 而不是 ArgumentException
1赞 DAre G 4/24/2017 #26

这两者都有效:

  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"

relativePath = "To_Folder"

output = http://tesrurl.test.com/Int18/To_Folder

以下代码将出现一些错误:

 // If you use the below code, some issues will be there in the final URI
 Uri final = new Uri(baseUrl, relativePath);
4赞 Nick N. 11/6/2017 #27

一个简单的一句话:

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

受到@Matt夏普回答的启发。

3赞 Andreas 11/17/2017 #28

为什么不直接使用以下内容。

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

评论

0赞 Underverse 2/6/2018
我正在寻找这个PowerShell版本,这将是: 但是这失败了,结果是: .从第二个 subPath 中删除,它就可以工作了:[System.IO.Path]::Combine("http://MyUrl.com/","/Images/Image.jpg")/Images/Image.jpg/[System.IO.Path]::Combine("http://MyUrl.com/","Images/Image.jpg")
0赞 pholpar 3/22/2018
好主意,但当其中一个参数为 null 时,它会失败。
0赞 pholpar 3/22/2018 #29

我们使用以下简单的帮助程序方法将任意数量的 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!

138赞 Michael Freidgeim 4/13/2018 #30

上面有 Todd Menier 的评论Flurl 包含一个 .Url.Combine

更多详情:

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

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

在 NuGet 上获取 Flurl.Http

PM>安装包 Flurl.Http

或者获取没有 HTTP 功能的独立 URL 构建器

PM> Install-Package Flurl

评论

10赞 Brian MacKay 11/28/2018
好吧,这个问题得到了很多流量,而 1000+ 赞成票的答案实际上并非在所有情况下都有效。多年后,我实际上为此使用了 Flurl,所以我接受了这个。它似乎在我遇到的所有情况下都有效。如果人们不想依赖,我发布了一个也可以正常工作的答案。
3赞 highboi 2/2/2019
如果您不使用并且会使用轻量级版本,github.com/jean-lourenco/UrlCombineFlurl
3赞 skippy 9/30/2018 #31

我发现构造函数将“\”翻转为“/”。因此,您也可以将 ,与构造函数一起使用。UriPath.CombineUri

 Uri baseUri = new Uri("http://MyUrl.com");
 string path = Path.Combine("Images", "Image.jpg");
 Uri myUri = new Uri(baseUri, path);
3赞 LawMan 3/26/2019 #32

值得一提的是,这里有几个扩展方法。第一个将组合路径,第二个将参数添加到 URL。

    public static string CombineUrl(this string root, string path, params string[] paths)
    {
        if (string.IsNullOrWhiteSpace(path))
        {
            return root;
        }

        Uri baseUri = new Uri(root);
        Uri combinedPaths = new Uri(baseUri, path);

        foreach (string extendedPath in paths)
        {
           combinedPaths = new Uri(combinedPaths, extendedPath);
        }

        return combinedPaths.AbsoluteUri;
    }

    public static string AddUrlParams(this string url, Dictionary<string, string> parameters)
    {
        if (parameters == null || !parameters.Keys.Any())
        {
            return url;
        }

        var tempUrl = new StringBuilder($"{url}?");
        int count = 0;

        foreach (KeyValuePair<string, string> parameter in parameters)
        {
            if (count > 0)
            {
                tempUrl.Append("&");
            }

            tempUrl.Append($"{WebUtility.UrlEncode(parameter.Key)}={WebUtility.UrlEncode(parameter.Value)}");
            count++;
        }

        return tempUrl.ToString();
    }
4赞 Neo 8/28/2019 #33

如果不想添加第三方依赖项(如 Flurl)或创建自定义扩展方法,可以在 ASP.NET Core(在 Microsoft.Owin 中也可用)中使用用于构建 URI 路径的扩展方法。然后,可以使用 this 和 的组合创建完整的 URI。PathStringUriUriBuilder

在本例中,它将是:

new Uri(new UriBuilder("http", "MyUrl.com").Uri, new PathString("/Images").Add("/Image.jpg").ToString())

这为您提供了所有组成部分,而无需在基 URL 中指定分隔符。不幸的是,要求在每个字符串前面加上它,否则它实际上会抛出一个 !但至少你可以以一种易于单元测试的方式确定性地构建你的 URI。PathString/ArgumentException

评论

0赞 Iain 4/6/2023
我试过了 - 1.它似乎要求你的第一个以 - 结尾 - 否则它将在附加之前获取此 URI 的根。2.doc 建议它基本上是关于正确处理转义,这在本例中不是问题。.Uri/PathString
6赞 Tobias Schwarzinger 9/4/2019 #34

所以我有另一种方法,类似于所有使用UriBuilder的人。

我不想像javajava那样拆分我的BaseUrl(可以包含路径的一部分 - 例如 http://mybaseurl.com/dev/)。

以下代码片段显示了代码 + 测试。

小心:此解决方案将主机小写并附加端口。如果不需要这样做,可以通过例如利用 的 属性来编写字符串表示。UriUriBuilder

  public class Tests
  {
         public static string CombineUrl (string baseUrl, string path)
         {
           var uriBuilder = new UriBuilder (baseUrl);
           uriBuilder.Path = Path.Combine (uriBuilder.Path, path);
           return uriBuilder.ToString();
         }

         [TestCase("http://MyUrl.com/", "/Images/Image.jpg", "http://myurl.com:80/Images/Image.jpg")]
         [TestCase("http://MyUrl.com/basePath", "/Images/Image.jpg", "http://myurl.com:80/Images/Image.jpg")]
         [TestCase("http://MyUrl.com/basePath", "Images/Image.jpg", "http://myurl.com:80/basePath/Images/Image.jpg")]
         [TestCase("http://MyUrl.com/basePath/", "Images/Image.jpg", "http://myurl.com:80/basePath/Images/Image.jpg")]
         public void Test1 (string baseUrl, string path, string expected)
         {
           var result = CombineUrl (baseUrl, path);

           Assert.That (result, Is.EqualTo (expected));
         }
  }

在 Windows 10 上使用 .NET Core 2.1 进行测试。

为什么这样做?

即使会返回反斜杠(至少在 Windows 上),UriBuilder 也会在 的 Setter 中处理这种情况。Path.CombinePath

取自 https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/src/System/UriBuilder.cs(注意对string.Replace)

[AllowNull]
public string Path
{
      get
      {
          return _path;
      }
      set
      {
          if ((value == null) || (value.Length == 0))
          {
              value = "/";
          }
          _path = Uri.InternalEscapeString(value.Replace('\\', '/'));
          _changed = true;
      }
 }

这是最好的方法吗?

当然,这个解决方案是相当自我描述的(至少在我看来)。但是您依赖于 .NET API 中未记录的(至少我在快速谷歌搜索中找不到任何内容)“功能”。这可能会在将来的版本中发生变化,因此请通过测试来涵盖方法。

https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/tests/FunctionalTests/UriBuilderTests.cs () 中有一些测试可以检查 是否正确转换。Path_Get_Set\

旁注:如果 uri 将用于 ctor,也可以直接使用该属性。UriBuilder.UriSystem.Uri

评论

0赞 aggsol 10/2/2019
这是一种非常可靠的方法。为单元测试竖起大拇指!!
6赞 DubDub 9/19/2019 #35

对于任何正在寻找单行代码并且只想在不创建新方法或引用新库或构造 URI 值并将其转换为字符串的情况下连接路径部分的人,那么......

string urlToImage = String.Join("/", "websiteUrl", "folder1", "folder2", "folder3", "item");

这是非常基本的,但我看不出你还需要什么。如果你害怕加倍的“/”,那么你可以简单地在之后做一个。如果您害怕替换“https://”中加倍的“//”,那么请做一个连接,替换加倍的“/”,然后加入网站 url(但是我很确定大多数浏览器会自动转换前面带有“https:”的任何内容以正确的格式读取)。这看起来像:.Replace("//", "/")

string urlToImage = String.Join("/","websiteUrl", String.Join("/", "folder1", "folder2", "folder3", "item").Replace("//","/"));

这里有很多答案可以处理上述所有问题,但就我而言,我只需要在一个地方使用一次,不需要严重依赖它。此外,很容易看出这里发生了什么。

请参见:https://learn.microsoft.com/en-us/dotnet/api/system.string.join?view=netframework-4.8

10赞 Mahmoud Hanafy 11/8/2019 #36

正如在其他答案中发现的那样,要么是新的,要么是可以做勾选的。 但是,基本 Uri 必须以 结尾,而相对 Uri 不能以 ; 开头否则将删除基 Url 的尾部Uri()TryCreate()//

我认为这最好作为一种扩展方法,即

public static Uri Append(this Uri uri, string relativePath)
{
    var baseUri = uri.AbsoluteUri.EndsWith('/') ? uri : new Uri(uri.AbsoluteUri + '/');
    var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;
    return new Uri(baseUri, relative);
}

并使用它:

var baseUri = new Uri("http://test.com/test/");
var combinedUri =  baseUri.Append("/Do/Something");

在性能方面,这消耗的资源比它需要的要多,因为 Uri 类会进行大量的解析和验证;一个非常粗略的分析(调试)在大约 2 秒内执行了一百万次操作。 这适用于大多数场景,但为了提高效率,最好将所有内容作为字符串进行操作,这需要 125 毫秒才能进行 100 万次操作。 即

public static string Append(this Uri uri, string relativePath)
{
    //avoid the use of Uri as it's not needed, and adds a bit of overhead.
    var absoluteUri = uri.AbsoluteUri; //a calculated property, better cache it
    var baseUri = absoluteUri.EndsWith('/') ? absoluteUri : absoluteUri + '/';
    var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;
    return baseUri + relative;
}

如果您仍想返回 URI,则 100 万次操作大约需要 600 毫秒。

public static Uri AppendUri(this Uri uri, string relativePath)
{
    //avoid the use of Uri as it's not needed, and adds a bit of overhead.
    var absoluteUri = uri.AbsoluteUri; //a calculated property, better cache it
    var baseUri = absoluteUri.EndsWith('/') ? absoluteUri : absoluteUri + '/';
    var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;
    return new Uri(baseUri + relative);
}

我希望这会有所帮助。

2赞 Filip Golewski 2/16/2020 #37

最近 Combine 方法已添加到 Energy.Core 包中,因此您可能希望使用它来联接 URL 部分。

    string url;
    url = Energy.Base.Url.Combine("https://www.youtube.com", "watch?v=NHCgbs3TcYg");
    Console.WriteLine(url);
    url = Energy.Base.Url.Combine("https://www.youtube.com", "watch?v=NHCgbs3TcYg", "t=150");
    Console.WriteLine(url);

此外,它将识别参数部分,因此它将按您的预期工作(使用斜杠连接路径,使用与号连接参数)。

https://www.youtube.com/watch?v=NHCgbs3TcYg

https://www.youtube.com/watch?v=NHCgbs3TcYg&t=150

Energy.Base.Url 类的文档

NuGet 库中的包Package on NuGet gallery

代码示例

13赞 GoldenAge 4/19/2020 #38

我认为这应该为您提供更大的灵活性,因为您可以根据需要处理任意数量的路径段:

public static string UrlCombine(this string baseUrl, params string[] segments)
=> string.Join("/", new[] { baseUrl.TrimEnd('/') }.Concat(segments.Select(s => s.Trim('/'))));
6赞 er sd 6/20/2020 #39

如果你不想有像 Flurl 这样的依赖项,你可以使用它的源代码:

    /// <summary>
    /// Basically a Path.Combine for URLs. Ensures exactly one '/' separates each segment,
    /// and exactly on '&amp;' separates each query parameter.
    /// URL-encodes illegal characters but not reserved characters.
    /// </summary>
    /// <param name="parts">URL parts to combine.</param>
    public static string Combine(params string[] parts) {
        if (parts == null)
            throw new ArgumentNullException(nameof(parts));

        string result = "";
        bool inQuery = false, inFragment = false;

        string CombineEnsureSingleSeparator(string a, string b, char separator) {
            if (string.IsNullOrEmpty(a)) return b;
            if (string.IsNullOrEmpty(b)) return a;
            return a.TrimEnd(separator) + separator + b.TrimStart(separator);
        }

        foreach (var part in parts) {
            if (string.IsNullOrEmpty(part))
                continue;

            if (result.EndsWith("?") || part.StartsWith("?"))
                result = CombineEnsureSingleSeparator(result, part, '?');
            else if (result.EndsWith("#") || part.StartsWith("#"))
                result = CombineEnsureSingleSeparator(result, part, '#');
            else if (inFragment)
                result += part;
            else if (inQuery)
                result = CombineEnsureSingleSeparator(result, part, '&');
            else
                result = CombineEnsureSingleSeparator(result, part, '/');

            if (part.Contains("#")) {
                inQuery = false;
                inFragment = true;
            }
            else if (!inFragment && part.Contains("?")) {
                inQuery = true;
            }
        }
        return EncodeIllegalCharacters(result);
    }

    /// <summary>
    /// URL-encodes characters in a string that are neither reserved nor unreserved. Avoids encoding reserved characters such as '/' and '?'. Avoids encoding '%' if it begins a %-hex-hex sequence (i.e. avoids double-encoding).
    /// </summary>
    /// <param name="s">The string to encode.</param>
    /// <param name="encodeSpaceAsPlus">If true, spaces will be encoded as + signs. Otherwise, they'll be encoded as %20.</param>
    /// <returns>The encoded URL.</returns>
    public static string EncodeIllegalCharacters(string s, bool encodeSpaceAsPlus = false) {
        if (string.IsNullOrEmpty(s))
            return s;

        if (encodeSpaceAsPlus)
            s = s.Replace(" ", "+");

        // Uri.EscapeUriString mostly does what we want - encodes illegal characters only - but it has a quirk
        // in that % isn't illegal if it's the start of a %-encoded sequence https://stackoverflow.com/a/47636037/62600

        // no % characters, so avoid the regex overhead
        if (!s.Contains("%"))
            return Uri.EscapeUriString(s);

        // pick out all %-hex-hex matches and avoid double-encoding 
        return Regex.Replace(s, "(.*?)((%[0-9A-Fa-f]{2})|$)", c => {
            var a = c.Groups[1].Value; // group 1 is a sequence with no %-encoding - encode illegal characters
            var b = c.Groups[2].Value; // group 2 is a valid 3-character %-encoded sequence - leave it alone!
            return Uri.EscapeUriString(a) + b;
        });
    }
4赞 user2368285 4/2/2021 #40

阅读以上所有示例,结果创建了我自己:

static string UrlCombine(params string[] items)
{
    if (items?.Any() != true)
    {
        return string.Empty;
    }

    return string.Join("/", items.Where(u => !string.IsNullOrWhiteSpace(u)).Select(u => u.Trim('/', '\\')));
}

用法

UrlCombine("https://microsoft.com","en-us")
5赞 Dave Black 12/2/2021 #41

我有一个无分配的字符串创建版本,我一直在使用它并取得了巨大的成功。

注意:

  1. 对于第一个字符串:它使用 - 修剪分隔符,因此仅从字符串的末尾开始。TrimEnd(separator)
  2. 对于余数:它使用 - 来修剪分隔符 - 所以路径的开始和结束Trim(separator)
  3. 它不附加尾部斜杠/分隔符。虽然可以进行简单的修改来添加此功能。

希望这有用!

/// <summary>
/// This implements an allocation-free string creation to construct the path.
/// This uses 3.5x LESS memory and is 2x faster than some alternate methods (StringBuilder, interpolation, string.Concat, etc.).
/// </summary>
/// <param name="str"></param>
/// <param name="paths"></param>
/// <returns></returns>
public static string ConcatPath(this string str, params string[] paths)
{
    const char separator = '/';
    if (str == null) throw new ArgumentNullException(nameof(str));

    var list = new List<ReadOnlyMemory<char>>();
    var first = str.AsMemory().TrimEnd(separator);

    // get length for intial string after it's trimmed
    var length = first.Length;
    list.Add(first);

    foreach (var path in paths)
    {
        var newPath = path.AsMemory().Trim(separator);
        length += newPath.Length + 1;
        list.Add(newPath);
    }

    var newString = string.Create(length, list, (chars, state) =>
    {
        // NOTE: We don't access the 'list' variable in this delegate since 
        // it would cause a closure and allocation. Instead we access the state parameter.

        // track our position within the string data we are populating
        var position = 0;

        // copy the first string data to index 0 of the Span<char>
        state[0].Span.CopyTo(chars);

        // update the position to the new length
        position += state[0].Span.Length;

        // start at index 1 when slicing
        for (var i = 1; i < state.Count; i++)
        {
            // add a separator in the current position and increment position by 1
            chars[position++] = separator;

            // copy each path string to a slice at current position
            state[i].Span.CopyTo(chars.Slice(position));

            // update the position to the new length
            position += state[i].Length;
        }
    });
    return newString;
}

使用 Benchmark DotNet 输出:

|                Method |     Mean |    Error |   StdDev |   Median | Ratio | RatioSD |  Gen 0 | Allocated |
|---------------------- |---------:|---------:|---------:|---------:|------:|--------:|-------:|----------:|
| ConcatPathWithBuilder | 404.1 ns | 27.35 ns | 78.48 ns | 380.3 ns |  1.00 |    0.00 | 0.3347 |   1,400 B |
|            ConcatPath | 187.2 ns |  5.93 ns | 16.44 ns | 183.2 ns |  0.48 |    0.10 | 0.0956 |     400 B |