using JNPF.Common.Security;
using JNPF.DependencyInjection;
using System.Diagnostics;
using System.Globalization;
using System.Text.Json;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
namespace JNPF.Common.Extension;
///
/// 字符串类型的扩展辅助操作类.
///
[SuppressSniffer]
public static class StringExtensions
{
#region 正则表达式
///
/// 指示所指定的正则表达式在指定的输入字符串中是否找到了匹配项.
///
/// 要搜索匹配项的字符串.
/// 要匹配的正则表达式模式.
/// 是否包含,否则全匹配.
/// 如果正则表达式找到匹配项,则为 true;否则,为 false.
public static bool IsMatch(this string value, string pattern, bool isContains = true)
{
if (value == null)
return false;
return isContains
? Regex.IsMatch(value, pattern)
: Regex.Match(value, pattern).Success;
}
///
/// 在指定的输入字符串中搜索指定的正则表达式的第一个匹配项.
///
/// 要搜索匹配项的字符串.
/// 要匹配的正则表达式模式.
/// 一个对象,包含有关匹配项的信息.
public static string Match(this string value, string pattern)
{
if (value == null)
return string.Empty;
return Regex.Match(value, pattern).Value;
}
///
/// 在指定的输入字符串中匹配并替换符合指定正则表达式的子串.
///
public static string ReplaceRegex(this string value, string pattern, string replacement)
{
if (value == null)
return string.Empty;
return Regex.Replace(value, pattern, replacement, RegexOptions.IgnoreCase);
}
///
/// 在指定的输入字符串中搜索指定的正则表达式的所有匹配项的字符串集合.
///
/// 要搜索匹配项的字符串.
/// 要匹配的正则表达式模式.
/// 一个集合,包含有关匹配项的字符串值.
public static IEnumerable Matches(this string value, string pattern)
{
if (value == null)
return new string[] { };
MatchCollection matches = Regex.Matches(value, pattern);
return from Match match in matches select match.Value;
}
///
/// 在指定的输入字符串中搜索指定的正则表达式的指定组匹配项的字符串.
///
/// 要搜索匹配项的字符串.
/// 要匹配的正则表达式模式.
/// 指定组的字符串.
/// 一个集合,包含有关匹配项的字符串值.
public static IEnumerable Matches(this string value, string pattern, string groups)
{
if (value == null)
return new string[] { };
MatchCollection matches = Regex.Matches(value, pattern);
return from Match match in matches select match.Groups[groups].Value;
}
///
/// 在指定的输入字符串中匹配第一个数字字符串.
///
public static string MatchFirstNumber(this string value)
{
MatchCollection matches = Regex.Matches(value, @"\d+");
if (matches.Count == 0)
return string.Empty;
return matches[0].Value;
}
///
/// 在指定字符串中匹配最后一个数字字符串.
///
public static string MatchLastNumber(this string value)
{
MatchCollection matches = Regex.Matches(value, @"\d+");
if (matches.Count == 0)
return string.Empty;
return matches[matches.Count - 1].Value;
}
///
/// 在指定字符串中匹配所有数字字符串.
///
public static IEnumerable MatchNumbers(this string value)
{
return Matches(value, @"\d+");
}
///
/// 检测指定字符串中是否包含数字.
///
public static bool IsMatchNumber(this string value)
{
return IsMatch(value, @"\d");
}
///
/// 检测指定字符串是否全部为数字并且长度等于指定长度.
///
public static bool IsMatchNumber(this string value, int length)
{
Regex regex = new Regex(@"^\d{" + length + "}$");
return regex.IsMatch(value);
}
///
/// 截取指定字符串之间的字符串.
///
///
/// 起始字符串.
/// 结束字符串,可多个.
/// 返回的中间字符串.
public static string Substring(this string source, string startString, params string[] endStrings)
{
if (source.IsMissing())
return string.Empty;
int startIndex = 0;
if (!string.IsNullOrEmpty(startString))
{
startIndex = source.IndexOf(startString, StringComparison.OrdinalIgnoreCase);
if (startIndex < 0)
throw new InvalidOperationException(string.Format("在源字符串中无法找到“{0}”的子串位置", startString));
startIndex = startIndex + startString.Length;
}
int endIndex = source.Length;
endStrings = endStrings.OrderByDescending(m => m.Length).ToArray();
foreach (string endString in endStrings)
{
if (string.IsNullOrEmpty(endString))
{
endIndex = source.Length;
break;
}
endIndex = source.IndexOf(endString, startIndex, StringComparison.OrdinalIgnoreCase);
if (endIndex < 0 || endIndex < startIndex)
{
continue;
}
break;
}
if (endIndex < 0 || endIndex < startIndex)
{
throw new InvalidOperationException(string.Format("在源字符串中无法找到“{0}”的子串位置", endStrings.ExpandAndToString()));
}
int length = endIndex - startIndex;
return source.Substring(startIndex, length);
}
///
/// 用正则表达式截取字符串.
///
public static string? Substring2(this string source, string startString, string endString)
{
return source.Substring2(startString, endString, false);
}
///
/// 用正则表达式截取字符串.
///
public static string? Substring2(this string source, string startString, string endString, bool containsEmpty)
{
if (source.IsMissing())
return string.Empty;
string inner = containsEmpty ? "\\s\\S" : "\\S";
string result = source.Match(string.Format("(?<={0})([{1}]+?)(?={2})", startString, inner, endString));
return result.IsMissing() ? null : result;
}
///
/// 截取{}中的字符串.
///
///
///
public static List Substring3(this string source)
{
MatchCollection mc = Regex.Matches(source, "(?i){.*?}");
return mc.Cast().Select(m => m.Value.TrimStart('{').TrimEnd('}')).ToList();
}
///
/// 是否电子邮件.
///
public static bool IsEmail(this string value)
{
const string pattern = @"^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$";
return value.IsMatch(pattern);
}
///
/// 是否是IP地址.
///
public static bool IsIpAddress(this string value)
{
const string pattern = @"^((?:(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d))))$";
return value.IsMatch(pattern);
}
///
/// 是否是整数.
///
public static bool IsNumeric(this string value)
{
const string pattern = @"^\-?[0-9]+$";
return value.IsMatch(pattern);
}
///
/// 是否是Unicode字符串.
///
public static bool IsUnicode(this string value)
{
const string pattern = @"^[\u4E00-\u9FA5\uE815-\uFA29]+$";
return value.IsMatch(pattern);
}
///
/// 是否Url字符串.
///
public static bool IsUrl(this string value)
{
try
{
if (value.IsNullOrEmpty() || value.Contains(' '))
return false;
Uri uri = new Uri(value);
return true;
}
catch (Exception)
{
return false;
}
}
///
/// 是否身份证号,验证如下3种情况:
/// 1.身份证号码为15位数字;
/// 2.身份证号码为18位数字;
/// 3.身份证号码为17位数字+1个字母.
///
public static bool IsIdentityCardId(this string value)
{
if (value.Length != 15 && value.Length != 18)
return false;
Regex regex;
string[] array;
DateTime time;
if (value.Length == 15)
{
regex = new Regex(@"^(\d{6})(\d{2})(\d{2})(\d{2})(\d{3})_");
if (!regex.Match(value).Success)
return false;
array = regex.Split(value);
return DateTime.TryParse(string.Format("{0}-{1}-{2}", "19" + array[2], array[3], array[4]), out time);
}
regex = new Regex(@"^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9Xx])$");
if (!regex.Match(value).Success)
return false;
array = regex.Split(value);
if (!DateTime.TryParse(string.Format("{0}-{1}-{2}", array[2], array[3], array[4]), out time))
return false;
// 校验最后一位
string[] chars = value.ToCharArray().Select(m => m.ToString()).ToArray();
int[] weights = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
int sum = 0;
for (int i = 0; i < 17; i++)
{
int num = int.Parse(chars[i]);
sum = sum + (num * weights[i]);
}
int mod = sum % 11;
// 检验码字符串
string vCode = "10X98765432";
string last = vCode.ToCharArray().ElementAt(mod).ToString();
return chars.Last().ToUpper() == last;
}
///
/// 是否手机号码.
///
///
/// 是否按严格格式验证.
public static bool IsMobileNumber(this string value, bool isRestrict = false)
{
string pattern = isRestrict ? @"^[1][3-8]\d{9}$" : @"^[1]\d{10}$";
return value.IsMatch(pattern);
}
#endregion
#region 其他操作
///
/// 指示指定的字符串是 null 或者 System.String.Empty 字符串.
///
[DebuggerStepThrough]
public static bool IsNullOrEmpty(this string value)
{
return string.IsNullOrEmpty(value);
}
///
/// 指示指定的字符串是 null、空或者仅由空白字符组成.
///
[DebuggerStepThrough]
public static bool IsNullOrWhiteSpace(this string value)
{
return string.IsNullOrWhiteSpace(value);
}
///
/// 指示指定的字符串是 null、空或者仅由空白字符组成.
///
[DebuggerStepThrough]
public static bool IsMissing(this string value)
{
return string.IsNullOrWhiteSpace(value);
}
///
/// 为指定格式的字符串填充相应对象来生成字符串.
///
/// 字符串格式,占位符以{n}表示.
/// 用于填充占位符的参数.
/// 格式化后的字符串.
[DebuggerStepThrough]
public static string FormatWith(this string format, params object[] args)
{
return string.Format(CultureInfo.CurrentCulture, format, args);
}
///
/// 将字符串反转.
///
/// 要反转的字符串.
public static string ReverseString(this string value)
{
return new string(value.Reverse().ToArray());
}
///
/// 单词变成单数形式.
///
///
///
public static string ToSingular(this string word)
{
Regex plural1 = new Regex("(?[^aeiou])ies$");
Regex plural2 = new Regex("(?[aeiou]y)s$");
Regex plural3 = new Regex("(?[sxzh])es$");
Regex plural4 = new Regex("(?[^sxzhyu])s$");
if (plural1.IsMatch(word))
return plural1.Replace(word, "${keep}y");
if (plural2.IsMatch(word))
return plural2.Replace(word, "${keep}");
if (plural3.IsMatch(word))
return plural3.Replace(word, "${keep}");
if (plural4.IsMatch(word))
return plural4.Replace(word, "${keep}");
return word;
}
///
/// 单词变成复数形式.
///
///
///
public static string ToPlural(this string word)
{
Regex plural1 = new Regex("(?[^aeiou])y$");
Regex plural2 = new Regex("(?[aeiou]y)$");
Regex plural3 = new Regex("(?[sxzh])$");
Regex plural4 = new Regex("(?[^sxzhy])$");
if (plural1.IsMatch(word))
return plural1.Replace(word, "${keep}ies");
if (plural2.IsMatch(word))
return plural2.Replace(word, "${keep}s");
if (plural3.IsMatch(word))
return plural3.Replace(word, "${keep}es");
if (plural4.IsMatch(word))
return plural4.Replace(word, "${keep}s");
return word;
}
///
/// 判断指定路径是否图片文件.
///
public static bool IsImageFile(this string filename)
{
if (!File.Exists(filename))
return false;
byte[] fileData = File.ReadAllBytes(filename);
if (fileData.Length == 0)
return false;
ushort code = BitConverter.ToUInt16(fileData, 0);
switch (code)
{
// bmp
case 0x4D42:
// jpg
case 0xD8FF:
// gif
case 0x4947:
// png
case 0x5089:
return true;
default:
return false;
}
}
///
/// 以指定字符串作为分隔符将指定字符串分隔成数组.
///
/// 要分割的字符串.
/// 字符串类型的分隔符.
/// 是否移除数据中元素为空字符串的项.
/// 分割后的数据.
public static string[] Split(this string value, string strSplit, bool removeEmptyEntries = false)
{
return value.Split(new[] { strSplit }, removeEmptyEntries ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None);
}
///
/// 获取字符串的MD5 Hash值.
///
public static string ToMd5Hash(this string value)
{
return HashHelper.GetMd5(value);
}
///
/// 支持汉字的字符串长度,汉字长度计为2.
///
/// 参数字符串.
/// 当前字符串的长度,汉字长度为2.
public static int TextLength(this string value)
{
ASCIIEncoding ascii = new ASCIIEncoding();
int tempLen = 0;
byte[] bytes = ascii.GetBytes(value);
foreach (byte b in bytes)
{
if (b == 63)
tempLen += 2;
else
tempLen += 1;
}
return tempLen;
}
///
/// 将JSON字符串还原为对象.
///
/// 要转换的目标类型.
/// JSON字符串.
///
public static T? FromJsonString(this string json) => JsonSerializer.Deserialize(json);
///
/// 将JSON字符串还原为对象.
///
/// JSON字符串.
/// 数据类型.
public static object? FromJsonString(this string json, Type type) => JsonSerializer.Deserialize(json, type);
///
/// 给URL添加查询参数.
///
/// URL字符串.
/// 要添加的参数,形如:"id=1,cid=2".
///
public static string AddUrlQuery(this string url, params string[] queries)
{
foreach (string query in queries)
{
if (!url.Contains('?'))
url += "?";
else if (!url.EndsWith("&"))
url += "&";
url = url + query;
}
return url;
}
///
/// 获取URL中指定参数的值,不存在返回空字符串.
///
public static string GetUrlQuery(this string url, string key)
{
Uri uri = new Uri(url);
string query = uri.Query;
if (query.IsNullOrEmpty())
return string.Empty;
query = query.TrimStart('?');
Dictionary? dict = (from m in query.Split("&", true)
let strs = m.Split("=")
select new KeyValuePair(strs[0], strs[1]))
.ToDictionary(m => m.Key, m => m.Value);
if (dict.ContainsKey(key))
return dict[key];
return string.Empty;
}
///
/// 给URL添加 # 参数.
///
/// URL字符串.
/// 要添加的参数.
///
public static string AddHashFragment(this string url, string query)
{
if (!url.Contains("#"))
url += "#";
return url + query;
}
///
/// 将字符串转换为[]数组,默认编码为.
///
public static byte[] ToBytes(this string value, Encoding? encoding = null)
{
if (encoding == null)
encoding = Encoding.UTF8;
return encoding.GetBytes(value);
}
///
/// 将[]数组转换为字符串,默认编码为.
///
public static string ToString2(this byte[] bytes, Encoding? encoding = null)
{
if (encoding == null)
encoding = Encoding.UTF8;
return encoding.GetString(bytes);
}
///
/// 将[]数组转换为Base64字符串.
///
public static string ToBase64String(this byte[] bytes)
{
return Convert.ToBase64String(bytes);
}
///
/// 将字符串转换为Base64字符串,默认编码为.
///
/// 正常的字符串.
/// 编码.
/// Base64字符串.
public static string ToBase64String(this string source, Encoding? encoding = null)
{
if (encoding == null)
encoding = Encoding.UTF8;
return Convert.ToBase64String(encoding.GetBytes(source));
}
///
/// 将Base64字符串转换为正常字符串,默认编码为.
///
/// Base64字符串.
/// 编码.
/// 正常字符串.
public static string FromBase64String(this string base64String, Encoding? encoding = null)
{
if (encoding == null)
encoding = Encoding.UTF8;
byte[] bytes = Convert.FromBase64String(base64String);
return encoding.GetString(bytes);
}
///
/// 将字符串进行UrlDecode解码.
///
/// 待UrlDecode解码的字符串.
/// UrlDecode解码后的字符串.
public static string ToUrlDecode(this string source)
{
return HttpUtility.UrlDecode(source);
}
///
/// 将字符串进行UrlEncode编码.
///
/// 待UrlEncode编码的字符串.
/// UrlEncode编码后的字符串.
public static string ToUrlEncode(this string source)
{
return HttpUtility.UrlEncode(source);
}
///
/// 将字符串进行HtmlDecode解码.
///
/// 待HtmlDecode解码的字符串.
/// HtmlDecode解码后的字符串.
public static string ToHtmlDecode(this string source)
{
return HttpUtility.HtmlDecode(source);
}
///
/// 将字符串进行HtmlEncode编码.
///
/// 待HtmlEncode编码的字符串.
/// HtmlEncode编码后的字符串.
public static string ToHtmlEncode(this string source)
{
return HttpUtility.HtmlEncode(source);
}
///
/// 将字符串转换为十六进制字符串,默认编码为.
///
public static string ToHexString(this string source, Encoding? encoding = null)
{
if (encoding == null)
encoding = Encoding.UTF8;
byte[] bytes = encoding.GetBytes(source);
return bytes.ToHexString();
}
///
/// 将十六进制字符串转换为常规字符串,默认编码为.
///
public static string FromHexString(this string hexString, Encoding? encoding = null)
{
if (encoding == null)
encoding = Encoding.UTF8;
byte[] bytes = hexString.ToHexBytes();
return encoding.GetString(bytes);
}
///
/// 将byte[]编码为十六进制字符串.
///
/// byte[]数组.
/// 十六进制字符串.
public static string ToHexString(this byte[] bytes)
{
return bytes.Aggregate(string.Empty, (current, t) => current + t.ToString("X2"));
}
///
/// 将十六进制字符串转换为byte[].
///
/// 十六进制字符串.
/// byte[]数组.
public static byte[] ToHexBytes(this string hexString)
{
hexString = hexString ?? string.Empty;
hexString = hexString.Replace(" ", string.Empty);
byte[] bytes = new byte[hexString.Length / 2];
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
return bytes;
}
///
/// 将字符串进行Unicode编码,变成形如“\u7f16\u7801”的形式.
///
/// 要进行编号的字符串.
public static string ToUnicodeString(this string source)
{
Regex regex = new Regex(@"[^\u0000-\u00ff]");
return regex.Replace(source, m => string.Format(@"\u{0:x4}", (short)m.Value[0]));
}
///
/// 将形如“\u7f16\u7801”的Unicode字符串解码.
///
public static string FromUnicodeString(this string source)
{
Regex regex = new Regex(@"\\u([0-9a-fA-F]{4})", RegexOptions.Compiled);
return regex.Replace(
source,
m =>
{
short s;
if (short.TryParse(m.Groups[1].Value, NumberStyles.HexNumber, CultureInfo.InstalledUICulture, out s))
return string.Empty + (char)s;
return m.Value;
});
}
///
/// 将驼峰字符串按单词拆分并转换成小写,再以特定字符串分隔.
///
/// 待转换的字符串.
/// 分隔符字符.
///
public static string UpperToLowerAndSplit(this string str, string splitStr = "-")
{
if (string.IsNullOrEmpty(str))
return str;
List words = new List();
while (str.Length > 0)
{
char c = str.FirstOrDefault(char.IsUpper);
if (c == default(char))
{
words.Add(str);
break;
}
int upperIndex = str.IndexOf(c);
// admin
if (upperIndex < 0)
return str;
// adminAdmin
if (upperIndex > 0)
{
string first = str.Substring(0, upperIndex);
words.Add(first);
str = str.Substring(upperIndex, str.Length - upperIndex);
continue;
}
str = char.ToLower(str[0]) + str.Substring(1, str.Length - 1);
}
return words.ExpandAndToString(splitStr);
}
///
/// 将驼峰字符串的第一个字符小写.
///
public static string ToLowerCase(this string str)
{
if (string.IsNullOrEmpty(str) || !char.IsUpper(str[0]))
return str;
if (str.Length == 1)
return char.ToLower(str[0]).ToString();
return char.ToLower(str[0]) + str.Substring(1, str.Length - 1);
}
///
/// 将小驼峰字符串的第一个字符大写.
///
public static string ToUpperCase(this string str)
{
if (string.IsNullOrEmpty(str) || !char.IsLower(str[0]))
return str;
if (str.Length == 1)
return char.ToUpper(str[0]).ToString();
return char.ToUpper(str[0]) + str.Substring(1, str.Length - 1);
}
///
/// Span实现首字符大写
/// added by ly on 20230407
///
///
///
public static string FirstCharToUpperAsSpan(this string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
Span destination = stackalloc char[1];
input.AsSpan(0, 1).ToUpperInvariant(destination);
return $"{destination}{input.AsSpan(1)}";
}
///
/// 计算当前字符串与指定字符串的编辑距离(相似度).
///
/// 源字符串.
/// 目标字符串.
/// 输出相似度.
/// 是否忽略大小写.
/// 编辑距离.
public static int LevenshteinDistance(this string source, string target, out double similarity, bool ignoreCase = false)
{
if (string.IsNullOrEmpty(source))
{
if (string.IsNullOrEmpty(target))
{
similarity = 1;
return 0;
}
similarity = 0;
return target.Length;
}
if (string.IsNullOrEmpty(target))
{
similarity = 0;
return source.Length;
}
string from, to;
if (ignoreCase)
{
from = source;
to = target;
}
else
{
from = source.ToLower();
to = source.ToLower();
}
int m = from.Length, n = to.Length;
int[,] mn = new int[m + 1, n + 1];
for (int i = 0; i <= m; i++)
{
mn[i, 0] = i;
}
for (int j = 1; j <= n; j++)
{
mn[0, j] = j;
}
for (int i = 1; i <= m; i++)
{
char c = from[i - 1];
for (int j = 1; j <= n; j++)
{
if (c == to[j - 1])
{
mn[i, j] = mn[i - 1, j - 1];
}
else
{
mn[i, j] = Math.Min(mn[i - 1, j - 1], Math.Min(mn[i - 1, j], mn[i, j - 1])) + 1;
}
}
}
int maxLength = Math.Max(m, n);
similarity = (double)(maxLength - mn[m, n]) / maxLength;
return mn[m, n];
}
///
/// 计算两个字符串的相似度,应用公式:相似度=kq*q/(kq*q+kr*r+ks*s)(kq>0,kr>=0,ka>=0)
/// 其中,q是字符串1和字符串2中都存在的单词的总数,s是字符串1中存在,字符串2中不存在的单词总数,r是字符串2中存在,字符串1中不存在的单词总数. kq,kr和ka分别是q,r,s的权重,根据实际的计算情况,我们设kq=2,kr=ks=1.
///
/// 源字符串.
/// 目标字符串.
/// 是否忽略大小写.
/// 字符串相似度.
public static double GetSimilarityWith(this string source, string target, bool ignoreCase = false)
{
if (string.IsNullOrEmpty(source) && string.IsNullOrEmpty(target))
return 1;
if (string.IsNullOrEmpty(source) || string.IsNullOrEmpty(target))
return 0;
const double kq = 2, kr = 1, ks = 1;
char[] sourceChars = source.ToCharArray(), targetChars = target.ToCharArray();
// 获取交集数量
int q = sourceChars.Intersect(targetChars).Count(), s = sourceChars.Length - q, r = targetChars.Length - q;
return kq * q / ((kq * q) + (kr * r) + (ks * s));
}
///
/// 字符串转枚举
///
/// 枚举类型
/// 字符串值
/// 枚举值
public static T ToEnum(this string value)
{
return (T)System.Enum.Parse(typeof(T), value, true);
}
#endregion
#region 转换,来自Abp
///
/// Converts PascalCase string to camelCase string.
///
/// String to convert
/// set true to use current culture. Otherwise, invariant culture will be used.
/// set true to if you want to convert 'XYZ' to 'xyz'.
/// camelCase of the string
public static string ToCamelCase(this string str, bool useCurrentCulture = false, bool handleAbbreviations = false)
{
if (string.IsNullOrWhiteSpace(str))
{
return str;
}
if (str.Length == 1)
{
return useCurrentCulture ? str.ToLower() : str.ToLowerInvariant();
}
if (handleAbbreviations && IsAllUpperCase(str))
{
return useCurrentCulture ? str.ToLower() : str.ToLowerInvariant();
}
return (useCurrentCulture ? char.ToLower(str[0]) : char.ToLowerInvariant(str[0])) + str.Substring(1);
}
///
/// Converts given PascalCase/camelCase string to sentence (by splitting words by space).
/// Example: "ThisIsSampleSentence" is converted to "This is a sample sentence".
///
/// String to convert.
/// set true to use current culture. Otherwise, invariant culture will be used.
public static string ToSentenceCase(this string str, bool useCurrentCulture = false)
{
if (string.IsNullOrWhiteSpace(str))
{
return str;
}
return useCurrentCulture
? Regex.Replace(str, "[a-z][A-Z]", m => m.Value[0] + " " + char.ToLower(m.Value[1]))
: Regex.Replace(str, "[a-z][A-Z]", m => m.Value[0] + " " + char.ToLowerInvariant(m.Value[1]));
}
///
/// Converts given PascalCase/camelCase string to kebab-case.
///
/// String to convert.
/// set true to use current culture. Otherwise, invariant culture will be used.
public static string ToKebabCase(this string str, bool useCurrentCulture = false)
{
if (string.IsNullOrWhiteSpace(str))
{
return str;
}
str = str.ToCamelCase();
return useCurrentCulture
? Regex.Replace(str, "[a-z][A-Z]", m => m.Value[0] + "-" + char.ToLower(m.Value[1]))
: Regex.Replace(str, "[a-z][A-Z]", m => m.Value[0] + "-" + char.ToLowerInvariant(m.Value[1]));
}
///
/// Converts given PascalCase/camelCase string to snake case.
/// Example: "ThisIsSampleSentence" is converted to "this_is_a_sample_sentence".
/// https://github.com/npgsql/npgsql/blob/dev/src/Npgsql/NameTranslation/NpgsqlSnakeCaseNameTranslator.cs#L51
///
/// String to convert.
///
public static string ToSnakeCase(this string str)
{
if (string.IsNullOrWhiteSpace(str))
{
return str;
}
var builder = new StringBuilder(str.Length + Math.Min(2, str.Length / 5));
var previousCategory = default(UnicodeCategory?);
for (var currentIndex = 0; currentIndex < str.Length; currentIndex++)
{
var currentChar = str[currentIndex];
if (currentChar == '_')
{
builder.Append('_');
previousCategory = null;
continue;
}
var currentCategory = char.GetUnicodeCategory(currentChar);
switch (currentCategory)
{
case UnicodeCategory.UppercaseLetter:
case UnicodeCategory.TitlecaseLetter:
if (previousCategory == UnicodeCategory.SpaceSeparator ||
previousCategory == UnicodeCategory.LowercaseLetter ||
previousCategory != UnicodeCategory.DecimalDigitNumber &&
previousCategory != null &&
currentIndex > 0 &&
currentIndex + 1 < str.Length &&
char.IsLower(str[currentIndex + 1]))
{
builder.Append('_');
}
currentChar = char.ToLower(currentChar);
break;
case UnicodeCategory.LowercaseLetter:
case UnicodeCategory.DecimalDigitNumber:
if (previousCategory == UnicodeCategory.SpaceSeparator)
{
builder.Append('_');
}
break;
default:
if (previousCategory != null)
{
previousCategory = UnicodeCategory.SpaceSeparator;
}
continue;
}
builder.Append(currentChar);
previousCategory = currentCategory;
}
return builder.ToString();
}
///
/// Converts camelCase string to PascalCase string.
///
/// String to convert
/// set true to use current culture. Otherwise, invariant culture will be used.
/// PascalCase of the string
public static string ToPascalCase(this string str, bool useCurrentCulture = false)
{
if (string.IsNullOrWhiteSpace(str))
{
return str;
}
if (str.Length == 1)
{
return useCurrentCulture ? str.ToUpper() : str.ToUpperInvariant();
}
return (useCurrentCulture ? char.ToUpper(str[0]) : char.ToUpperInvariant(str[0])) + str.Substring(1);
}
///
/// Removes first occurrence of the given prefixes from beginning of the given string.
///
/// The string.
/// one or more prefix.
/// Modified string or the same string if it has not any of given prefixes
public static string RemovePreFix(this string str, params string[] preFixes)
{
return str.RemovePreFix(StringComparison.Ordinal, preFixes);
}
///
/// Removes first occurrence of the given prefixes from beginning of the given string.
///
/// The string.
/// String comparison type
/// one or more prefix.
/// Modified string or the same string if it has not any of given prefixes
public static string RemovePreFix(this string str, StringComparison comparisonType, params string[] preFixes)
{
if (str.IsNullOrEmpty())
{
return str;
}
if (preFixes.IsNullOrEmpty())
{
return str;
}
foreach (var preFix in preFixes)
{
if (str.StartsWith(preFix, comparisonType))
{
return str.Right(str.Length - preFix.Length);
}
}
return str;
}
///
/// Removes first occurrence of the given postfixes from end of the given string.
///
/// The string.
/// one or more postfix.
/// Modified string or the same string if it has not any of given postfixes
public static string RemovePostFix(this string str, params string[] postFixes)
{
return str.RemovePostFix(StringComparison.Ordinal, postFixes);
}
///
/// Removes first occurrence of the given postfixes from end of the given string.
///
/// The string.
/// String comparison type
/// one or more postfix.
/// Modified string or the same string if it has not any of given postfixes
public static string RemovePostFix(this string str, StringComparison comparisonType, params string[] postFixes)
{
if (str.IsNullOrEmpty())
{
return str;
}
if (postFixes.IsNullOrEmpty())
{
return str;
}
foreach (var postFix in postFixes)
{
if (str.EndsWith(postFix, comparisonType))
{
return str.Left(str.Length - postFix.Length);
}
}
return str;
}
///
/// Gets a substring of a string from beginning of the string.
///
/// Thrown if is null
/// Thrown if is bigger that string's length
public static string Left(this string str, int len)
{
if (str.Length < len)
{
throw new ArgumentException("len argument can not be bigger than given string's length!");
}
return str.Substring(0, len);
}
///
/// Gets a substring of a string from end of the string.
///
/// Thrown if is null
/// Thrown if is bigger that string's length
public static string Right(this string str, int len)
{
if (str.Length < len)
{
throw new ArgumentException("len argument can not be bigger than given string's length!");
}
return str.Substring(str.Length - len, len);
}
private static bool IsAllUpperCase(string input)
{
for (int i = 0; i < input.Length; i++)
{
if (Char.IsLetter(input[i]) && !Char.IsUpper(input[i]))
{
return false;
}
}
return true;
}
#endregion
}