提问人:Brian MacKay 提问时间:12/17/2008 最后编辑:Peter MortensenBrian MacKay 更新时间:6/28/2022 访问量:474129
Path.Combine 用于 URL?
Path.Combine for URLs?
问:
Path.Combine 很方便,但是 .NET Framework 中是否有类似的 URL 函数?
我正在寻找这样的语法:
Url.Combine("http://MyUrl.com/", "/Images/Image.jpg")
这将返回:
"http://MyUrl.com/Images/Image.jpg"
答:
您使用: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
评论
int.TryParse
DateTime.TryParseExact
test.com/mydirectory/
/helloworld.aspx
test.com/helloworld.aspx
诙谐的例子,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");
}
这可能是一个适当的简单解决方案:
public static string Combine(string uri1, string uri2)
{
uri1 = uri1.TrimEnd('/');
uri2 = uri2.TrimStart('/');
return string.Format("{0}/{1}", uri1, uri2);
}
评论
string
Path.xxx()
Uri
有一个构造函数,应该为你执行此操作:new Uri(Uri baseUri, string relativeUri)
下面是一个示例:
Uri baseUri = new Uri("http://www.contoso.com");
Uri myUri = new Uri(baseUri, "catalog/shownew.htm");
编者注:请注意,此方法无法按预期工作。在某些情况下,它可以剪切部分 baseUri。请参阅评论和其他答案。
评论
根据您提供的示例 URL,我假设您要合并与您的网站相关的 URL。
基于这个假设,我将提出这个解决方案作为对你的问题的最合适的回答,即:“Path.Combinable很方便,URL框架中是否有类似的功能?
由于 URL 框架中有一个类似的函数,我建议正确的是:“VirtualPathUtility.Combine”方法。 下面是 MSDN 参考链接:VirtualPathUtility.Combine 方法
有一点需要注意:我认为这仅适用于与您的网站相关的 URL(也就是说,您不能使用它来生成指向另一个网站的链接。例如,)。var url = VirtualPathUtility.Combine("www.google.com", "accounts/widgets");
评论
Server.MapPath
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
评论
ArgumentNullException("url1")
Nothing
将它们组合起来并确保它始终正确的一种简单方法是:
string.Format("{0}/{1}", Url1.Trim('/'), Url2);
评论
我只是把一个小的扩展方法放在一起:
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.Replace(Path.DirectorySeparatorChar, '/');
path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
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")
评论
这里已经有一些很好的答案。根据 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 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);
}
评论
用:
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()
无论哪种情况,您最终都可能返回不正确的结果,因为这些方法会不断截断第一个参数的相对部分,即从 to .baseUri
http://google.com/some/thing
http://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 中找到
我有单元测试来涵盖三种最常见的情况:
评论
好吧,我只是连接两个字符串并使用正则表达式来完成清理部分。
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 的 (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
评论
我用这段代码来解决这个问题:
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]);
}
}
}
将 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();
});
我创建了这个功能,可以让你的生活更轻松:
/// <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
我发现以下内容很有用,并具有以下功能:
- 抛出 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
评论
这两者都有效:
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);
一个简单的一句话:
public static string Combine(this string uri1, string uri2) => $"{uri1.TrimEnd('/')}/{uri2.TrimStart('/')}";
受到@Matt夏普回答的启发。
为什么不直接使用以下内容。
System.IO.Path.Combine(rootUrl, subPath).Replace(@"\", "/")
评论
[System.IO.Path]::Combine("http://MyUrl.com/","/Images/Image.jpg")
/Images/Image.jpg
/
[System.IO.Path]::Combine("http://MyUrl.com/","Images/Image.jpg")
我们使用以下简单的帮助程序方法将任意数量的 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!
上面有 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"
PM>安装包 Flurl.Http
或者获取没有 HTTP 功能的独立 URL 构建器:
PM> Install-Package Flurl
评论
我发现构造函数将“\”翻转为“/”。因此,您也可以将 ,与构造函数一起使用。Uri
Path.Combine
Uri
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.NET Core(在 Microsoft.Owin 中也可用)中使用用于构建 URI 路径的扩展方法。然后,可以使用 this 和 的组合创建完整的 URI。PathString
Uri
UriBuilder
在本例中,它将是:
new Uri(new UriBuilder("http", "MyUrl.com").Uri, new PathString("/Images").Add("/Image.jpg").ToString())
这为您提供了所有组成部分,而无需在基 URL 中指定分隔符。不幸的是,要求在每个字符串前面加上它,否则它实际上会抛出一个 !但至少你可以以一种易于单元测试的方式确定性地构建你的 URI。PathString
/
ArgumentException
评论
.Uri
/
PathString
所以我有另一种方法,类似于所有使用UriBuilder的人。
我不想像javajava那样拆分我的BaseUrl(可以包含路径的一部分 - 例如 http://mybaseurl.com/dev/)。
以下代码片段显示了代码 + 测试。
小心:此解决方案将主机小写并附加端口。如果不需要这样做,可以通过例如利用 的 属性来编写字符串表示。Uri
UriBuilder
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.Combine
Path
取自 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.Uri
System.Uri
评论
对于任何正在寻找单行代码并且只想在不创建新方法或引用新库或构造 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
正如在其他答案中发现的那样,要么是新的,要么是可以做勾选的。
但是,基本 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);
}
我希望这会有所帮助。
最近 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);
此外,它将识别参数部分,因此它将按您的预期工作(使用斜杠连接路径,使用与号连接参数)。
NuGet 库中的包Package on NuGet gallery
我认为这应该为您提供更大的灵活性,因为您可以根据需要处理任意数量的路径段:
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 '&' 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;
});
}
阅读以上所有示例,结果创建了我自己:
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")
我有一个无分配的字符串创建版本,我一直在使用它并取得了巨大的成功。
注意:
- 对于第一个字符串:它使用 - 修剪分隔符,因此仅从字符串的末尾开始。
TrimEnd(separator)
- 对于余数:它使用 - 来修剪分隔符 - 所以路径的开始和结束
Trim(separator)
- 它不附加尾部斜杠/分隔符。虽然可以进行简单的修改来添加此功能。
希望这有用!
/// <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 |
评论
Url.Combine
Uri