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);}
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 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
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;
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);}
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]);}}}
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
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");
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);}
/// <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, '&');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;});}
/// <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;}