diff --git a/SageTools/Extension/Stream.cs b/SageTools/Extension/Stream.cs new file mode 100644 index 0000000..93d08c8 --- /dev/null +++ b/SageTools/Extension/Stream.cs @@ -0,0 +1,35 @@ +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace SageTools.Extension +{ + public static partial class Extension + { + /// + /// 将流读为字符串 + /// 注:默认使用UTF-8编码 + /// + /// 流 + /// 指定编码 + /// + public static async Task ReadToStringAsync(this Stream stream, Encoding encoding = null) + { + encoding ??= Encoding.UTF8; + + if (stream.CanSeek) + { + stream.Seek(0, SeekOrigin.Begin); + } + + var resStr = await new StreamReader(stream, encoding).ReadToEndAsync(); + + if (stream.CanSeek) + { + stream.Seek(0, SeekOrigin.Begin); + } + + return resStr; + } + } +} \ No newline at end of file diff --git a/SageTools/Extension/String.cs b/SageTools/Extension/String.cs index a137f95..5212dd1 100644 --- a/SageTools/Extension/String.cs +++ b/SageTools/Extension/String.cs @@ -1,11 +1,17 @@ using System; using System.Collections.Generic; using System.Data; +using System.Globalization; using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Web; +using System.Xml.Linq; +using System.Xml; namespace SageTools.Extension { @@ -24,14 +30,17 @@ public static string SubSpecifiedLengthStr(this string str, int length, bool ret { return str; } + if (length == 0) { return string.Empty; } + if (length < 0 || length > str.Length) { return returnAllOrThrowWhenIndexOutOfRange ? str : throw new IndexOutOfRangeException(); } + return str.Substring(length); } @@ -109,6 +118,7 @@ public static string ReplaceByEmpty(this string str, params string[] values) { str = str.Replace(oldValue, ""); } + return str; } @@ -310,7 +320,8 @@ public static string Md5(this string str) /// /// 是否是正确的email地址 /// - public static bool IsValidEmail(this string obj) => Regex.IsMatch(obj, "^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$"); + public static bool IsValidEmail(this string obj) => + Regex.IsMatch(obj, "^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$"); /// /// 是否是正确的ip地址 @@ -343,14 +354,14 @@ public static bool IsUrl(this string strUrl) => Regex.IsMatch(strUrl, /// /// 满足条件则添加 /// - public static StringBuilder AppendIf(this StringBuilder @this, Func predicate, params T[] values) { - foreach (T obj in values) + foreach (var obj in values) { if (predicate(obj)) @this.Append((object) obj); } + return @this; } @@ -359,11 +370,12 @@ public static StringBuilder AppendIf(this StringBuilder @this, Func /// public static StringBuilder AppendLineIf(this StringBuilder @this, Func predicate, params T[] values) { - foreach (T obj in values) + foreach (var obj in values) { if (predicate(obj)) @this.AppendLine(obj.ToString()); } + return @this; } @@ -404,5 +416,470 @@ public static StringBuilder AppendLineFormat(this StringBuilder @this, string fo /// 从右截取指定字符,超出长度则截取到开头 /// public static string Right(this string @this, int length) => @this.RightSafe(length); + + /// + /// 转换为日期格式 + /// + public static DateTime ToDateTime(this string @this) => Convert.ToDateTime(@this); + + /// + /// 转为字节数组 + /// + /// base64字符串 + /// + public static byte[] ToBytes_FromBase64Str(this string base64Str) + { + return Convert.FromBase64String(base64Str); + } + + /// + /// 转换为MD5加密后的字符串(默认加密为32位) + /// + /// + /// + public static string ToMD5String(this string str) + { + var md5 = MD5.Create(); + var inputBytes = Encoding.UTF8.GetBytes(str); + var hashBytes = md5.ComputeHash(inputBytes); + + var sb = new StringBuilder(); + foreach (var t in hashBytes) + { + sb.Append(t.ToString("x2")); + } + + md5.Dispose(); + + return sb.ToString(); + } + + /// + /// 转换为MD5加密后的字符串(16位) + /// + /// + /// + public static string ToMD5String16(this string str) + { + return str.ToMD5String().Substring(8, 16); + } + + /// + /// Base64加密 + /// 注:默认采用UTF8编码 + /// + /// 待加密的明文 + /// 加密后的字符串 + public static string Base64Encode(this string source) + { + return Base64Encode(source, Encoding.UTF8); + } + + /// + /// Base64加密 + /// + /// 待加密的明文 + /// 加密采用的编码方式 + /// + public static string Base64Encode(this string source, Encoding encoding) + { + string encode; + var bytes = encoding.GetBytes(source); + try + { + encode = Convert.ToBase64String(bytes); + } + catch + { + encode = source; + } + + return encode; + } + + /// + /// Base64解密 + /// 注:默认使用UTF8编码 + /// + /// 待解密的密文 + /// 解密后的字符串 + public static string Base64Decode(this string result) + { + return Base64Decode(result, Encoding.UTF8); + } + + /// + /// Base64解密 + /// + /// 待解密的密文 + /// 解密采用的编码方式,注意和加密时采用的方式一致 + /// 解密后的字符串 + public static string Base64Decode(this string result, Encoding encoding) + { + string decode; + var bytes = Convert.FromBase64String(result); + try + { + decode = encoding.GetString(bytes); + } + catch + { + decode = result; + } + + return decode; + } + + /// + /// Base64Url编码 + /// + /// 待编码的文本字符串 + /// 编码的文本字符串 + public static string Base64UrlEncode(this string text) + { + var plainTextBytes = Encoding.UTF8.GetBytes(text); + var base64 = Convert.ToBase64String(plainTextBytes).Replace('+', '-').Replace('/', '_').TrimEnd('='); + + return base64; + } + + /// + /// Base64Url解码 + /// + /// 使用Base64Url编码后的字符串 + /// 解码后的内容 + public static string Base64UrlDecode(this string base64UrlStr) + { + base64UrlStr = base64UrlStr.Replace('-', '+').Replace('_', '/'); + switch (base64UrlStr.Length % 4) + { + case 2: + base64UrlStr += "=="; + break; + case 3: + base64UrlStr += "="; + break; + } + + var bytes = Convert.FromBase64String(base64UrlStr); + + return Encoding.UTF8.GetString(bytes); + } + + /// + /// 计算SHA1摘要 + /// 注:默认使用UTF8编码 + /// + /// 字符串 + /// + public static byte[] ToSHA1Bytes(this string str) + { + return str.ToSHA1Bytes(Encoding.UTF8); + } + + /// + /// 计算SHA1摘要 + /// + /// 字符串 + /// 编码 + /// + public static byte[] ToSHA1Bytes(this string str, Encoding encoding) + { + SHA1 sha1 = new SHA1CryptoServiceProvider(); + var inputBytes = encoding.GetBytes(str); + var outputBytes = sha1.ComputeHash(inputBytes); + + return outputBytes; + } + + /// + /// 转为SHA1哈希加密字符串 + /// 注:默认使用UTF8编码 + /// + /// 字符串 + /// + public static string ToSHA1String(this string str) + { + return str.ToSHA1String(Encoding.UTF8); + } + + /// + /// 转为SHA1哈希 + /// + /// 字符串 + /// 编码 + /// + public static string ToSHA1String(this string str, Encoding encoding) + { + var sha1Bytes = str.ToSHA1Bytes(encoding); + var resStr = BitConverter.ToString(sha1Bytes); + return resStr.Replace("-", "").ToLower(); + } + + /// + /// SHA256加密 + /// + /// 字符串 + /// + public static string ToSHA256String(this string str) + { + var bytes = Encoding.UTF8.GetBytes(str); + var hash = SHA256.Create().ComputeHash(bytes); + + var builder = new StringBuilder(); + foreach (var t in hash) + { + builder.Append(t.ToString("x2")); + } + + return builder.ToString(); + } + + /// + /// HMACSHA256算法 + /// + /// 内容 + /// 密钥 + /// + public static string ToHMACSHA256String(this string text, string secret) + { + secret ??= ""; + var keyByte = Encoding.UTF8.GetBytes(secret); + var messageBytes = Encoding.UTF8.GetBytes(text); + using var hmacsha256 = new HMACSHA256(keyByte); + var hashmessage = hmacsha256.ComputeHash(messageBytes); + return Convert.ToBase64String(hashmessage).Replace('+', '-').Replace('/', '_').TrimEnd('='); + } + + /// + /// string转int + /// + /// 字符串 + /// + public static int ToInt(this string str) + { + str = str.Replace("\0", ""); + if (string.IsNullOrEmpty(str)) + return 0; + return Convert.ToInt32(str); + } + + /// + /// string转long + /// + /// 字符串 + /// + public static long ToLong(this string str) + { + str = str.Replace("\0", ""); + if (string.IsNullOrEmpty(str)) + return 0; + + return Convert.ToInt64(str); + } + + /// + /// 二进制字符串转为Int + /// + /// 二进制字符串 + /// + public static int ToInt_FromBinString(this string str) + { + return Convert.ToInt32(str, 2); + } + + /// + /// 将16进制字符串转为Int + /// + /// 数值 + /// + public static int ToInt0X(this string str) + { + int num = Int32.Parse(str, NumberStyles.HexNumber); + return num; + } + + /// + /// 转换为double + /// + /// 字符串 + /// + public static double ToDouble(this string str) + { + return Convert.ToDouble(str); + } + + /// + /// string转byte[] + /// + /// 字符串 + /// + public static byte[] ToBytes(this string str) + { + return Encoding.Default.GetBytes(str); + } + + /// + /// string转byte[] + /// + /// 字符串 + /// 需要的编码 + /// + public static byte[] ToBytes(this string str, Encoding theEncoding) + { + return theEncoding.GetBytes(str); + } + + /// + /// 将16进制字符串转为Byte数组 + /// + /// 16进制字符串(2个16进制字符表示一个Byte) + /// + public static byte[] To0XBytes(this string str) + { + List resBytes = new List(); + for (int i = 0; i < str.Length; i = i + 2) + { + string numStr = $@"{str[i]}{str[i + 1]}"; + resBytes.Add((byte) numStr.ToInt0X()); + } + + return resBytes.ToArray(); + } + + /// + /// 将ASCII码形式的字符串转为对应字节数组 + /// 注:一个字节一个ASCII码字符 + /// + /// 字符串 + /// + public static byte[] ToASCIIBytes(this string str) + { + return str.ToList().Select(x => (byte) x).ToArray(); + } + + /// + /// 删除Json字符串中键中的@符号 + /// + /// json字符串 + /// + public static string RemoveAt(this string jsonStr) + { + Regex reg = new Regex("\"@([^ \"]*)\"\\s*:\\s*\"(([^ \"]+\\s*)*)\""); + string strPatten = "\"$1\":\"$2\""; + return reg.Replace(jsonStr, strPatten); + } + + /// + /// json数据转实体类,仅仅应用于单个实体类,速度非常快 + /// + /// 泛型参数 + /// json字符串 + /// + public static T ToEntity(this string json) + { + if (string.IsNullOrEmpty(json)) + return default(T); + + var type = typeof(T); + var obj = Activator.CreateInstance(type, null); + + foreach (var item in type.GetProperties()) + { + var info = obj.GetType().GetProperty(item.Name); + var pattern = "\"" + item.Name + "\":\"(.*?)\""; + foreach (Match match in Regex.Matches(json, pattern)) + { + switch (item.PropertyType.ToString()) + { + case "System.String": + info?.SetValue(obj, match.Groups[1].ToString(), null); + break; + case "System.Int32": + info?.SetValue(obj, match.Groups[1].ToString().ToInt(), null); + break; + case "System.Int64": + info?.SetValue(obj, Convert.ToInt64(match.Groups[1].ToString()), null); + break; + case "System.DateTime": + info?.SetValue(obj, Convert.ToDateTime(match.Groups[1].ToString()), null); + break; + } + } + } + + return (T) obj; + } + + /// + /// 转为首字母大写 + /// + /// 字符串 + /// + public static string ToFirstUpperStr(this string str) + { + return str[..1].ToUpper() + str[1..]; + } + + /// + /// 转为首字母小写 + /// + /// 字符串 + /// + public static string ToFirstLowerStr(this string str) + { + return str[..1].ToLower() + str[1..]; + } + + /// + /// 转为网络终结点IPEndPoint + /// = + /// 字符串 + /// + public static IPEndPoint ToIPEndPoint(this string str) + { + IPEndPoint iPEndPoint = null; + try + { + var strArray = str.Split(':').ToArray(); + var addr = strArray[0]; + var port = Convert.ToInt32(strArray[1]); + iPEndPoint = new IPEndPoint(IPAddress.Parse(addr), port); + } + catch + { + iPEndPoint = null; + } + + return iPEndPoint; + } + + /// + /// 将枚举类型的文本转为枚举类型 + /// + /// 枚举类型 + /// 枚举文本 + /// + public static TEnum ToEnum(this string enumText) where TEnum : struct + { + System.Enum.TryParse(enumText, out TEnum value); + + return value; + } + + /// + /// 是否为弱密码 + /// 注:密码必须包含数字、小写字母、大写字母和其他符号中的两种并且长度大于8 + /// + /// 密码 + /// + public static bool IsWeakPwd(this string pwd) + { + if (pwd.IsNullOrEmpty()) + throw new Exception("pwd不能为空"); + + const string pattern = "(^[0-9]+$)|(^[a-z]+$)|(^[A-Z]+$)|(^.{0,8}$)"; + return Regex.IsMatch(pwd, pattern); + } } } \ No newline at end of file diff --git a/SageTools/Utils/ExtensionUtils.cs b/SageTools/Utils/ExtensionUtils.cs new file mode 100644 index 0000000..5f8ff5e --- /dev/null +++ b/SageTools/Utils/ExtensionUtils.cs @@ -0,0 +1,113 @@ +using System; +using System.Threading.Tasks; + +namespace SageTools.Utils +{ + /// + /// 工具类扩展方法类 + /// + public class ExtensionUtils + { + /// + /// 同步等待重试 + /// + /// + /// 执行方法 + /// 跳出重试条件,当为true时,终止重试 + /// 重试次数 + /// 重试间隔时间(ms) + /// 执行出错 + public static T DelayRetry(Func executeFunc, Func breakConditionFunc, int retryCount = 1, int milliseconds = 1000) + { + var res = default(T); + while (retryCount > 0) + { + retryCount--; + res = executeFunc.Invoke(); + if (breakConditionFunc.Invoke(res)) break; + Task.Delay(milliseconds).Wait(); + } + + return res; + } + + /// + /// 异步等待重试 + /// + /// + /// 执行方法 + /// 跳出重试条件,当为true时,终止重试 + /// 重试次数 + /// 重试间隔时间(ms) + /// 执行出错 + public static async Task DelayRetryAsync(Func> executeFunc, Func breakConditionFunc, int retryCount = 1, int milliseconds = 1000) + { + var res = default(T); + while (retryCount > 0) + { + retryCount--; + res = await executeFunc.Invoke(); + if (breakConditionFunc.Invoke(res)) break; + await Task.Delay(milliseconds); + } + + return res; + } + + /// + ///同步方法重试(异常情况也重试) + /// + /// + /// 执行的方法 + /// 跳出重试条件,当为true时,终止重试 + /// 重试次数 + /// + public static T ActionRetry(Func action, Func breakConditionFunc, int retryCount = 1) + { + var res = default(T); + while (retryCount > 0) + { + retryCount--; + try + { + res = action.Invoke(); + if (breakConditionFunc.Invoke(res)) break; + } + catch + { + // ignore + } + } + + return res; + } + + /// + /// 异步方法重试(异常情况也重试) + /// + /// + /// 执行的方法 + /// 跳出重试条件,当为true时,终止重试 + /// 重试次数 + /// + public static async Task ActionRetryAsync(Func> action, Func breakConditionFunc, int retryCount = 1) + { + var res = default(T); + while (retryCount > 0) + { + retryCount--; + try + { + res = await action.Invoke(); + if (breakConditionFunc.Invoke(res)) break; + } + catch + { + // ignore + } + } + + return res; + } + } +} \ No newline at end of file