Path.Combine很方便,但是.NET框架中是否有类似的URL函数?
我正在寻找这样的语法:
Url.Combine("http://MyUrl.com/", "/Images/Image.jpg")
这将返回:
"http://MyUrl.com/Images/Image.jpg"
Path.Combine很方便,但是.NET框架中是否有类似的URL函数?
我正在寻找这样的语法:
Url.Combine("http://MyUrl.com/", "/Images/Image.jpg")
这将返回:
"http://MyUrl.com/Images/Image.jpg"
当前回答
我有一个无需分配的字符串创建版本,我一直在成功地使用它。
注:
对于第一个字符串:它使用TrimEnd(分隔符)修剪分隔符,因此仅从字符串的末尾开始。对于余数:它使用Trim(分隔符)修剪分隔符-因此路径的起点和终点都是它不附加尾部斜杠/分隔符。虽然可以做一个简单的修改来增加这个能力。
希望你觉得这很有用!
/// <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;
}
基准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 |
其他回答
我发现Uri构造函数将“\”转换为“/”。因此,您也可以将Path.Combine与Uri构造函数一起使用。
Uri baseUri = new Uri("http://MyUrl.com");
string path = Path.Combine("Images", "Image.jpg");
Uri myUri = new Uri(baseUri, path);
如果任何人正在寻找一个单行,并且只想在不创建新方法或引用新库的情况下连接路径的一部分,或者构造URI值并将其转换为字符串,那么。。。
string urlToImage = String.Join("/", "websiteUrl", "folder1", "folder2", "folder3", "item");
这很基本,但我看不出你还需要什么。如果你害怕“/”加倍,那么你可以简单地在后面做一个替换(“//”,“/”)。如果您害怕替换“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
所以我有另一种方法,类似于使用UriBuilder的每个人。
我不想拆分我的BaseUrl(它可以包含路径的一部分,例如。http://mybaseurl.com/dev/)正如javajavajavajava所做的那样。
下面的代码片段显示了代码+测试。
注意:此解决方案降低了主机并附加了一个端口。如果不需要这样做,可以通过例如利用UriBuilder的Uri属性来编写字符串表示。
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在路径设置器中处理这种情况。
摘自https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/src/System/UriBuilder.cs(注意调用字符串。替换)
[AllowNull]
public string Path
{
get
{
return _path;
}
set
{
if ((value == null) || (value.Length == 0))
{
value = "/";
}
_path = Uri.InternalEscapeString(value.Replace('\\', '/'));
_changed = true;
}
}
这是最好的方法吗?
当然,这个解决方案非常自我描述(至少在我看来)。但是,您依赖的是.NETAPI中的未记录(至少我在快速谷歌搜索中没有发现任何内容)“功能”。这可能会随着将来的版本而改变,因此请将方法与测试结合起来。
中有测试https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/tests/FunctionalTests/UriBuilderTests.cs(Path_Get_Set),用于检查\是否正确转换。
附带说明:如果Uri将用于System.Uri构造函数,则也可以直接使用UriBuilder.Uri属性。
好吧,我只是连接两个字符串并使用正则表达式来完成清理部分。
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(this string uri1, string uri2) => $"{uri1.TrimEnd('/')}/{uri2.TrimStart('/')}";
灵感来自@Matt Sharpe的回答。