路径。组合为 URL?

组合 很方便,但是在 网址的.NET 框架中有类似的函数吗?

我在寻找这样的语法:

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

它将返回:

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

436389 次浏览

您使用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

有趣的例子,Ryan,以指向函数的链接结束。干得好。

一个建议Brian:如果您将此代码包装在函数中,您可能希望在TryCreate调用之前使用UriBuilder包装基本URL。

否则,基本URL必须包含方案(其中UriBuilder将假设超文本传输协议://)。只是一个想法:

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();elsethrow new ArgumentException("Unable to combine specified url values");}

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

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

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

这里有一个例子:

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

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

根据您提供的示例url,我假设您想组合相对于您的站点的URL。

基于这个假设,我将提出这个解决方案作为对您的问题的最合适的回答:“Path. Combine很方便,URL框架中是否有类似函数?”

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

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

Ryan Cook的答案与我所追求的很接近,可能更适合其他开发人员。但是,它在字符串的开头添加了超文本传输协议://,并且通常它比我追求的格式多一点。

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

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 StringIf url1.Length = 0 ThenReturn url2End If
If url2.Length = 0 ThenReturn url1End 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

组合它们并确保它始终正确的简单方法是:

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

我必须指出,Path.Combine似乎也直接为此工作,至少在. NET 4上。

我只是整理了一个小扩展方法:

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");
Path.Combine("Http://MyUrl.com/", "/Images/Image.jpg").Replace("\\", "/")

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

我首先尝试了新的Uri(Uri baseUri, string relativeUri)方法,但由于像http://www.mediawiki.org/wiki/Special:SpecialPages这样的URI而失败:

new Uri(new Uri("http://www.mediawiki.org/wiki/"), "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")

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

使用这个:

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

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

VirtualPathUtility.Combine

我把前面所有的答案结合起来:

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

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

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

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

这是我的方法,我也会为自己使用它:

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 seperatorif (string.IsNullOrEmpty(part1) || string.IsNullOrEmpty(part2)){seperator = string.Empty;}
// If part1 is not empty,// remove '/' at lastif (!string.IsNullOrEmpty(part1)){newPart1 = part1.TrimEnd('/');}
// If part2 is not empty,// remove '/' at firstif (!string.IsNullOrEmpty(part2)){newPart2 = part2.TrimStart('/');}
// Now finally combinereturn string.Format("{0}{1}{2}", newPart1, seperator, newPart2);}

用途:

    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

组合URL的多个部分可能有点棘手。您可以使用双参数构造函数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中找到,以演示用法

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

在此处输入图像描述

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

    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"

我的通用解决方案:

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

以下是Microsoft的(Office Dev PnP)方法非常有用。合并

    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

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

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 frontif (!brokenBaseUrl.Contains(brokenRootFolderPath[x])){if (x == 0){RootLocationUrl = Context.Url.TrimEnd('/');}else{RootLocationUrl += String.Format("/{0}", brokenRootFolderPath[x]);}}}

将URL与URI组合时的规则

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

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

如果您遵循上述规则,您可以将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();});

我创建了这个功能,这将使您的生活更轻松:

    /// <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 pathsConsole.WriteLine(PathCombine(true , true , true , @"\/\/folder 1\/\/\/\\/\folder2\///folder3\\/" , @"/\somefile.ext\/\//\"));// Result: /folder 1/folder2/folder3/somefile.ext
// Doesn't fix internal pathsConsole.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 pathsConsole.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

我发现以下内容很有用,并具有以下特点:

  • 抛出空或空白
  • 为多个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

这两个工作:

  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 URIUri final = new Uri(baseUrl, relativePath);

一个简单的单行:

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

灵感来自@Matt Sharpe的回答。

为什么不使用下面的方法呢?

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

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

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

请注意,它不支持'…/…/page.htm'风格的相对URL!

是Todd Menier的评论Flurl包括Url.Combine

更多细节:

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

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

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

PM>安装包更新

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

 Uri baseUri = new Uri("http://MyUrl.com");string path = Path.Combine("Images", "Image.jpg");Uri myUri = new Uri(baseUri, path);

值得一提的是,这里有几个扩展方法。第一个将组合路径,第二个将参数添加到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();}

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

在这种情况下,它将是:

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

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

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

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

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

注意:此解决方案对主机进行小写并附加一个端口。如果不需要,可以通过例如利用UriBuilderUri属性来编写字符串表示。

  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进行测试。

为什么这工作吗?

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

取自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.csPath_Get_Set)中有测试,检查\是否正确转换。

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

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

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

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

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

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

我认为这最好作为一种扩展方法来完成,即。

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类进行了大量的解析和验证;一个非常粗略的分析(Debug)在大约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 itvar 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 itvar baseUri = absoluteUri.EndsWith('/') ? absoluteUri : absoluteUri + '/';var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;return new Uri(baseUri + relative);}

我希望这能有所帮助。

最近结合方法被添加到能量核心包中,因此您可能希望使用它来加入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画廊

代码示例

我认为这应该给你更多的灵活性,因为你可以处理尽可能多的路径段:

public static string UrlCombine(this string baseUrl, params string[] segments)=> string.Join("/", new[] { baseUrl.TrimEnd('/') }.Concat(segments.Select(s => s.Trim('/'))));

如果你不想有像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, '&');elseresult = 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 overheadif (!s.Contains("%"))return Uri.EscapeUriString(s);
// pick out all %-hex-hex matches and avoid double-encodingreturn 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 charactersvar b = c.Groups[2].Value; // group 2 is a valid 3-character %-encoded sequence - leave it alone!return Uri.EscapeUriString(a) + b;});}

//阅读所有上述示例,结果创建了我的自我:

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")

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

注:

  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 trimmedvar 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 populatingvar 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 lengthposition += state[0].Span.Length;
// start at index 1 when slicingfor (var i = 1; i < state.Count; i++){// add a separator in the current position and increment position by 1chars[position++] = separator;
// copy each path string to a slice at current positionstate[i].Span.CopyTo(chars.Slice(position));
// update the position to the new lengthposition += 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 |