2022-12-07 14:05:11 +01:00
|
|
|
using System.Globalization;
|
2022-12-12 16:11:49 +01:00
|
|
|
using System.Net;
|
|
|
|
|
using System.Text;
|
2022-12-07 14:05:11 +01:00
|
|
|
using System.Text.RegularExpressions;
|
2026-04-02 15:19:54 +02:00
|
|
|
using Microsoft.AspNetCore.Html;
|
2022-12-07 14:05:11 +01:00
|
|
|
|
2026-04-07 14:23:29 +02:00
|
|
|
namespace Finch.Extensions;
|
2022-12-07 14:05:11 +01:00
|
|
|
|
|
|
|
|
public static class StringExtensions
|
|
|
|
|
{
|
|
|
|
|
const string SPACE = " ";
|
|
|
|
|
|
|
|
|
|
static Regex replaceMultipleSpacesRegex { get; } = new Regex("[ ]{2,}", RegexOptions.None);
|
|
|
|
|
|
|
|
|
|
static Regex newLineCharsRegex { get; } = new Regex("\t|\n|\r", RegexOptions.None);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string FullTrim(this string value)
|
|
|
|
|
{
|
2025-02-25 11:31:32 +01:00
|
|
|
if (String.IsNullOrEmpty(value))
|
2022-12-07 14:05:11 +01:00
|
|
|
{
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return replaceMultipleSpacesRegex.Replace(value, SPACE).Trim();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string EnsureStartsWith(this string input, string toStartWith)
|
|
|
|
|
{
|
|
|
|
|
if (input.StartsWith(toStartWith)) return input;
|
|
|
|
|
return toStartWith + input.TrimStart(toStartWith);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string EnsureStartsWith(this string input, char value)
|
|
|
|
|
{
|
|
|
|
|
return input.StartsWith(value.ToString(CultureInfo.InvariantCulture)) ? input : value + input;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string EnsureEndsWith(this string input, char value)
|
|
|
|
|
{
|
|
|
|
|
return input.EndsWith(value.ToString(CultureInfo.InvariantCulture)) ? input : input + value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string EnsureEndsWith(this string input, string toEndWith)
|
|
|
|
|
{
|
|
|
|
|
return input.EndsWith(toEndWith.ToString(CultureInfo.InvariantCulture)) ? input : input + toEndWith;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string EnsureSurroundedWith(this string input, char toSurroundWith)
|
|
|
|
|
{
|
|
|
|
|
return input.EnsureStartsWith(toSurroundWith).EnsureEndsWith(toSurroundWith);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string Or(this string value, string fallback)
|
|
|
|
|
{
|
2025-02-25 11:31:32 +01:00
|
|
|
return String.IsNullOrWhiteSpace(value) ? fallback : value;
|
2022-12-07 14:05:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string TrimEnd(this string value, string forRemoving)
|
|
|
|
|
{
|
2025-02-25 11:31:32 +01:00
|
|
|
if (String.IsNullOrEmpty(value)) return value;
|
|
|
|
|
if (String.IsNullOrEmpty(forRemoving)) return value;
|
2022-12-07 14:05:11 +01:00
|
|
|
|
|
|
|
|
while (value.EndsWith(forRemoving, StringComparison.InvariantCultureIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
value = value.Remove(value.LastIndexOf(forRemoving, StringComparison.InvariantCultureIgnoreCase));
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string TrimStart(this string value, string forRemoving)
|
|
|
|
|
{
|
2025-02-25 11:31:32 +01:00
|
|
|
if (String.IsNullOrEmpty(value)) return value;
|
|
|
|
|
if (String.IsNullOrEmpty(forRemoving)) return value;
|
2022-12-07 14:05:11 +01:00
|
|
|
|
|
|
|
|
while (value.StartsWith(forRemoving, StringComparison.InvariantCultureIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
value = value.Substring(forRemoving.Length);
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static bool HasValue(this string input)
|
|
|
|
|
{
|
2025-02-25 11:31:32 +01:00
|
|
|
return !String.IsNullOrWhiteSpace(input);
|
2022-12-07 14:05:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static bool IsNullOrEmpty(this string input)
|
|
|
|
|
{
|
2025-02-25 11:31:32 +01:00
|
|
|
return String.IsNullOrEmpty(input);
|
2022-12-07 14:05:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static bool IsNullOrWhiteSpace(this string input)
|
|
|
|
|
{
|
2025-02-25 11:31:32 +01:00
|
|
|
return String.IsNullOrWhiteSpace(input);
|
2022-12-07 14:05:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string ToCamelCase(this string input)
|
|
|
|
|
{
|
2025-02-25 11:31:32 +01:00
|
|
|
if (!String.IsNullOrEmpty(input) && input.Length > 1)
|
2022-12-07 14:05:11 +01:00
|
|
|
{
|
2025-02-25 11:31:32 +01:00
|
|
|
return Char.ToLowerInvariant(input[0]) + input.Substring(1);
|
2022-12-07 14:05:11 +01:00
|
|
|
}
|
|
|
|
|
return input;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string ToPascalCase(this string input)
|
|
|
|
|
{
|
2025-02-25 11:31:32 +01:00
|
|
|
if (!String.IsNullOrEmpty(input) && input.Length > 1)
|
2022-12-07 14:05:11 +01:00
|
|
|
{
|
2025-02-25 11:31:32 +01:00
|
|
|
return Char.ToUpperInvariant(input[0]) + input.Substring(1);
|
2022-12-07 14:05:11 +01:00
|
|
|
}
|
|
|
|
|
return input;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static string ToCamelCaseId(this string input)
|
|
|
|
|
{
|
2025-02-25 11:31:32 +01:00
|
|
|
if (String.IsNullOrEmpty(input))
|
2022-12-07 14:05:11 +01:00
|
|
|
{
|
|
|
|
|
return input;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (input.Length < 2)
|
|
|
|
|
{
|
|
|
|
|
return input.ToLowerInvariant();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string[] parts = input.Split('.');
|
|
|
|
|
|
2025-02-25 11:31:32 +01:00
|
|
|
return String.Join(".", parts.Select(x => x.ToCamelCase()));
|
2022-12-07 14:05:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string ToPascalCaseId(this string input)
|
|
|
|
|
{
|
2025-02-25 11:31:32 +01:00
|
|
|
if (String.IsNullOrEmpty(input))
|
2022-12-07 14:05:11 +01:00
|
|
|
{
|
|
|
|
|
return input;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (input.Length < 2)
|
|
|
|
|
{
|
|
|
|
|
return input.ToUpperInvariant();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string[] parts = input.Split('.');
|
|
|
|
|
|
2025-02-25 11:31:32 +01:00
|
|
|
return String.Join(".", parts.Select(x => x.ToPascalCase()));
|
2022-12-07 14:05:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string RemoveNewLines(this string input)
|
|
|
|
|
{
|
2025-02-25 11:31:32 +01:00
|
|
|
if (String.IsNullOrEmpty(input))
|
2022-12-07 14:05:11 +01:00
|
|
|
{
|
|
|
|
|
return input;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-25 11:31:32 +01:00
|
|
|
return newLineCharsRegex.Replace(input, String.Empty).Trim();
|
2022-12-07 14:05:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string Shorten(this string input, int maxLength)
|
|
|
|
|
{
|
|
|
|
|
if (maxLength < 4)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentOutOfRangeException("maxLength", "Has to be at least 4 characters long");
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-25 11:31:32 +01:00
|
|
|
if (String.IsNullOrEmpty(input) || input.Length <= maxLength)
|
2022-12-07 14:05:11 +01:00
|
|
|
{
|
|
|
|
|
return input;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return input.Substring(0, maxLength - 3) + "...";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string ReplaceFirst(this string text, string search, string replace)
|
|
|
|
|
{
|
|
|
|
|
int pos = text.IndexOf(search);
|
|
|
|
|
if (pos < 0)
|
|
|
|
|
{
|
|
|
|
|
return text;
|
|
|
|
|
}
|
|
|
|
|
return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
|
|
|
|
|
}
|
2022-12-12 16:11:49 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string NewLinesToBr(this string source)
|
|
|
|
|
{
|
|
|
|
|
StringBuilder builder = new();
|
|
|
|
|
string[] lines = source.Split('\n');
|
|
|
|
|
for (int i = 0; i < lines.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
if (i > 0)
|
|
|
|
|
{
|
|
|
|
|
builder.Append("<br/>");
|
|
|
|
|
}
|
|
|
|
|
builder.Append(WebUtility.HtmlEncode(lines[i]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return builder.ToString();
|
|
|
|
|
}
|
2024-03-26 14:52:04 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
public static string NewLinesToParagraphs(this string source, string classes = null)
|
|
|
|
|
{
|
|
|
|
|
StringBuilder builder = new();
|
|
|
|
|
string classAttribute = classes.HasValue() ? $" class=\"{classes}\"" : string.Empty;
|
|
|
|
|
builder.Append($"<p{classAttribute}>");
|
|
|
|
|
string[] lines = source.Split('\n');
|
|
|
|
|
for (int i = 0; i < lines.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
if (i > 0)
|
|
|
|
|
{
|
|
|
|
|
builder.Append($"</p><p{classAttribute}>");
|
|
|
|
|
}
|
|
|
|
|
builder.Append(WebUtility.HtmlEncode(lines[i]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
builder.Append("</p>");
|
|
|
|
|
return builder.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2022-12-13 16:25:36 +01:00
|
|
|
public static string FormatTwoFactorAuthenticationKey(this string unformattedKey)
|
|
|
|
|
{
|
|
|
|
|
var result = new StringBuilder();
|
|
|
|
|
int currentPosition = 0;
|
|
|
|
|
while (currentPosition + 4 < unformattedKey.Length)
|
|
|
|
|
{
|
|
|
|
|
result.Append(unformattedKey.AsSpan(currentPosition, 4)).Append(' ');
|
|
|
|
|
currentPosition += 4;
|
|
|
|
|
}
|
|
|
|
|
if (currentPosition < unformattedKey.Length)
|
|
|
|
|
{
|
|
|
|
|
result.Append(unformattedKey.AsSpan(currentPosition));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result.ToString().ToLowerInvariant();
|
|
|
|
|
}
|
2024-08-21 15:53:32 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
public static IEnumerable<string> Chunks(this string text, int groupSize)
|
|
|
|
|
{
|
|
|
|
|
ArgumentException.ThrowIfNullOrEmpty(text);
|
|
|
|
|
|
|
|
|
|
if (groupSize < 1)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < text.Length; i += groupSize)
|
|
|
|
|
{
|
|
|
|
|
if (groupSize + i > text.Length)
|
|
|
|
|
{
|
|
|
|
|
groupSize = text.Length - i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
yield return text.Substring(i, groupSize);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-02 15:19:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
public static IHtmlContent ToHtmlEntities(this string text)
|
|
|
|
|
{
|
|
|
|
|
StringBuilder sb = new();
|
|
|
|
|
|
|
|
|
|
foreach (char ch in text)
|
|
|
|
|
{
|
|
|
|
|
sb.Append("&#");
|
|
|
|
|
sb.Append((int)ch);
|
|
|
|
|
sb.Append(';');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HtmlContentBuilder builder = new();
|
|
|
|
|
builder.SetHtmlContent(sb.ToString());
|
|
|
|
|
return builder;
|
|
|
|
|
}
|
2022-12-07 14:05:11 +01:00
|
|
|
}
|