Przeglądaj źródła

半全角函数

懒得勤快 2 lat temu
rodzic
commit
e51d3110e1

+ 919 - 843
Masuit.Tools.Abstractions/Extensions/BaseType/StringExtensions.cs

@@ -16,852 +16,928 @@ using System.Threading.Tasks;
 
 namespace Masuit.Tools
 {
-	public static partial class StringExtensions
-	{
-		public static string Join(this IEnumerable<string> strs, string separate = ", ", bool removeEmptyEntry = false) => string.Join(separate, removeEmptyEntry ? strs.Where(s => !string.IsNullOrEmpty(s)) : strs);
-
-		public static string Join<T>(this IEnumerable<T> strs, string separate = ", ", bool removeEmptyEntry = false) => string.Join(separate, removeEmptyEntry ? strs.Where(t => t != null) : strs);
-
-		/// <summary>
-		/// 字符串转时间
-		/// </summary>
-		/// <param name="value"></param>
-		/// <returns></returns>
-		public static DateTime ToDateTime(this string value)
-		{
-			DateTime.TryParse(value, out var result);
-			return result;
-		}
-
-		/// <summary>
-		/// 字符串转Guid
-		/// </summary>
-		/// <param name="s"></param>
-		/// <returns></returns>
-		public static Guid ToGuid(this string s)
-		{
-			return Guid.Parse(s);
-		}
-
-		/// <summary>
-		/// 根据正则替换
-		/// </summary>
-		/// <param name="input"></param>
-		/// <param name="regex">正则表达式</param>
-		/// <param name="replacement">新内容</param>
-		/// <returns></returns>
-		public static string Replace(this string input, Regex regex, string replacement)
-		{
-			return regex.Replace(input, replacement);
-		}
-
-		/// <summary>
-		/// 生成唯一短字符串
-		/// </summary>
-		/// <param name="str"></param>
-		/// <param name="chars">可用字符数数量,0-9,a-z,A-Z</param>
-		/// <returns></returns>
-		public static string CreateShortToken(this string str, byte chars = 36)
-		{
-			var nf = new NumberFormater(chars);
-			return nf.ToString((DateTime.Now.Ticks - 630822816000000000) * 100 + Stopwatch.GetTimestamp() % 100);
-		}
-
-		/// <summary>
-		/// 任意进制转十进制
-		/// </summary>
-		/// <param name="str"></param>
-		/// <param name="base">进制</param>
-		/// <returns></returns>
-		public static long FromBase(this string str, byte @base)
-		{
-			var nf = new NumberFormater(@base);
-			return nf.FromString(str);
-		}
-
-		/// <summary>
-		/// 任意进制转大数十进制
-		/// </summary>
-		/// <param name="str"></param>
-		/// <param name="base">进制</param>
-		/// <returns></returns>
-		public static BigInteger FromBaseBig(this string str, byte @base)
-		{
-			var nf = new NumberFormater(@base);
-			return nf.FromStringBig(str);
-		}
-
-		#region 检测字符串中是否包含列表中的关键词
-
-		/// <summary>
-		/// 检测字符串中是否包含列表中的关键词(快速匹配)
-		/// </summary>
-		/// <param name="s">源字符串</param>
-		/// <param name="keys">关键词列表</param>
-		/// <param name="ignoreCase">忽略大小写</param>
-		/// <returns></returns>
-		public static bool Contains(this string s, IEnumerable<string> keys, bool ignoreCase = true)
-		{
-			if (keys is not ICollection<string> array)
-			{
-				array = keys.ToArray();
-			}
-
-			if (array.Count == 0 || string.IsNullOrEmpty(s))
-			{
-				return false;
-			}
-
-			return ignoreCase ? array.Any(item => s.IndexOf(item, StringComparison.InvariantCultureIgnoreCase) >= 0) : array.Any(s.Contains);
-		}
-
-		/// <summary>
-		/// 检测字符串中是否包含列表中的关键词(安全匹配)
-		/// </summary>
-		/// <param name="s">源字符串</param>
-		/// <param name="keys">关键词列表</param>
-		/// <param name="ignoreCase">忽略大小写</param>
-		/// <returns></returns>
-		public static bool ContainsSafety(this string s, IEnumerable<string> keys, bool ignoreCase = true)
-		{
-			if (keys is not ICollection<string> array)
-			{
-				array = keys.ToArray();
-			}
-
-			if (array.Count == 0 || string.IsNullOrEmpty(s))
-			{
-				return false;
-			}
-
-			bool flag = false;
-			if (ignoreCase)
-			{
-				foreach (var item in array)
-				{
-					if (s.Contains(item))
-					{
-						flag = true;
-					}
-				}
-			}
-			else
-			{
-				foreach (var item in array)
-				{
-					if (s.IndexOf(item, StringComparison.InvariantCultureIgnoreCase) >= 0)
-					{
-						flag = true;
-					}
-				}
-			}
-
-			return flag;
-		}
-
-		/// <summary>
-		/// 检测字符串中是否以列表中的关键词结尾
-		/// </summary>
-		/// <param name="s">源字符串</param>
-		/// <param name="keys">关键词列表</param>
-		/// <param name="ignoreCase">忽略大小写</param>
-		/// <returns></returns>
-		public static bool EndsWith(this string s, IEnumerable<string> keys, bool ignoreCase = true)
-		{
-			if (keys is not ICollection<string> array)
-			{
-				array = keys.ToArray();
-			}
-
-			if (array.Count == 0 || string.IsNullOrEmpty(s))
-			{
-				return false;
-			}
-
-			var pattern = $"({array.Select(Regex.Escape).Join("|")})$";
-			return ignoreCase ? Regex.IsMatch(s, pattern, RegexOptions.IgnoreCase) : Regex.IsMatch(s, pattern);
-		}
-
-		/// <summary>
-		/// 检测字符串中是否以列表中的关键词开始
-		/// </summary>
-		/// <param name="s">源字符串</param>
-		/// <param name="keys">关键词列表</param>
-		/// <param name="ignoreCase">忽略大小写</param>
-		/// <returns></returns>
-		public static bool StartsWith(this string s, IEnumerable<string> keys, bool ignoreCase = true)
-		{
-			if (keys is not ICollection<string> array)
-			{
-				array = keys.ToArray();
-			}
-
-			if (array.Count == 0 || string.IsNullOrEmpty(s))
-			{
-				return false;
-			}
-
-			var pattern = $"^({array.Select(Regex.Escape).Join("|")})";
-			return ignoreCase ? Regex.IsMatch(s, pattern, RegexOptions.IgnoreCase) : Regex.IsMatch(s, pattern);
-		}
-
-		/// <summary>
-		/// 检测字符串中是否包含列表中的关键词
-		/// </summary>
-		/// <param name="s">源字符串</param>
-		/// <param name="regex">关键词列表</param>
-		/// <param name="ignoreCase">忽略大小写</param>
-		/// <returns></returns>
-		public static bool RegexMatch(this string s, string regex, bool ignoreCase = true)
-		{
-			if (string.IsNullOrEmpty(regex) || string.IsNullOrEmpty(s))
-			{
-				return false;
-			}
-
-			if (ignoreCase)
-			{
-				return Regex.IsMatch(s, regex, RegexOptions.IgnoreCase);
-			}
-
-			return Regex.IsMatch(s, regex);
-		}
-
-		/// <summary>
-		/// 检测字符串中是否包含列表中的关键词
-		/// </summary>
-		/// <param name="s">源字符串</param>
-		/// <param name="regex">关键词列表</param>
-		/// <returns></returns>
-		public static bool RegexMatch(this string s, Regex regex) => !string.IsNullOrEmpty(s) && regex.IsMatch(s);
-
-		#endregion 检测字符串中是否包含列表中的关键词
-
-		/// <summary>
-		/// 判断字符串是否为空或""
-		/// </summary>
-		/// <param name="s"></param>
-		/// <returns></returns>
-		public static bool IsNullOrEmpty(this string s)
-		{
-			return string.IsNullOrWhiteSpace(s) || s.Equals("null", StringComparison.CurrentCultureIgnoreCase);
-		}
-
-		/// <summary>
-		/// 判断字符串不为空或""
-		/// </summary>
-		/// <param name="s"></param>
-		/// <returns></returns>
-		public static bool NotNullOrEmpty(this string s)
-		{
-			return !string.IsNullOrWhiteSpace(s) && !s.Equals("null", StringComparison.CurrentCultureIgnoreCase);
-		}
-
-		/// <summary>
-		/// 转成非null
-		/// </summary>
-		/// <param name="s"></param>
-		/// <returns></returns>
-		public static string AsNotNull(this string s)
-		{
-			return string.IsNullOrEmpty(s) ? "" : s;
-		}
-
-		/// <summary>
-		/// 转成非null
-		/// </summary>
-		/// <param name="s"></param>
-		/// <param name="value">为空时的替换值</param>
-		/// <returns></returns>
-		public static string IfNullOrEmpty(this string s, string value)
-		{
-			return string.IsNullOrEmpty(s) ? value : s;
-		}
-
-		/// <summary>
-		/// 转成非null
-		/// </summary>
-		/// <param name="s"></param>
-		/// <param name="valueFactory">为空时的替换值函数</param>
-		/// <returns></returns>
-		public static string IfNullOrEmpty(this string s, Func<string> valueFactory)
-		{
-			return string.IsNullOrEmpty(s) ? valueFactory() : s;
-		}
-
-		/// <summary>
-		/// 字符串掩码
-		/// </summary>
-		/// <param name="s">字符串</param>
-		/// <param name="mask">掩码符</param>
-		/// <returns></returns>
-		public static string Mask(this string s, char mask = '*')
-		{
-			if (string.IsNullOrWhiteSpace(s?.Trim()))
-			{
-				return s;
-			}
-			s = s.Trim();
-			string masks = mask.ToString().PadLeft(4, mask);
-			return s.Length switch
-			{
-				>= 11 => Regex.Replace(s, "(.{3}).*(.{4})", $"$1{masks}$2"),
-				10 => Regex.Replace(s, "(.{3}).*(.{3})", $"$1{masks}$2"),
-				9 => Regex.Replace(s, "(.{2}).*(.{3})", $"$1{masks}$2"),
-				8 => Regex.Replace(s, "(.{2}).*(.{2})", $"$1{masks}$2"),
-				7 => Regex.Replace(s, "(.{1}).*(.{2})", $"$1{masks}$2"),
-				6 => Regex.Replace(s, "(.{1}).*(.{1})", $"$1{masks}$2"),
-				_ => Regex.Replace(s, "(.{1}).*", $"$1{masks}")
-			};
-		}
-
-		#region Email
-
-		/// <summary>
-		/// 匹配Email
-		/// </summary>
-		/// <param name="s">源字符串</param>
-		/// <param name="valid">是否验证有效性</param>
-		/// <returns>匹配对象;是否匹配成功,若返回true,则会得到一个Match对象,否则为null</returns>
-		public static (bool isMatch, Match match) MatchEmail(this string s, bool valid = false)
-		{
-			if (string.IsNullOrEmpty(s) || s.Length < 7)
-			{
-				return (false, null);
-			}
-
-			var match = Regex.Match(s, @"[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n]+");
-			var isMatch = match.Success;
-			if (isMatch && valid)
-			{
-				var nslookup = new LookupClient();
-				var task = nslookup.Query(s.Split('@')[1], QueryType.MX).Answers.MxRecords().SelectAsync(r => Dns.GetHostAddressesAsync(r.Exchange.Value).ContinueWith(t =>
-				{
-					if (t.IsCanceled || t.IsFaulted)
-					{
-						return new[] { IPAddress.Loopback };
-					}
-
-					return t.Result;
-				}));
-				isMatch = task.Result.SelectMany(a => a).Any(ip => !ip.IsPrivateIP());
-			}
-
-			return (isMatch, match);
-		}
-
-		/// <summary>
-		/// 匹配Email
-		/// </summary>
-		/// <param name="s">源字符串</param>
-		/// <param name="valid">是否验证有效性</param>
-		/// <returns>匹配对象;是否匹配成功,若返回true,则会得到一个Match对象,否则为null</returns>
-		public static async Task<(bool isMatch, Match match)> MatchEmailAsync(this string s, bool valid = false)
-		{
-			if (string.IsNullOrEmpty(s) || s.Length < 7)
-			{
-				return (false, null);
-			}
-
-			var match = Regex.Match(s, @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");
-			var isMatch = match.Success;
-			if (isMatch && valid)
-			{
-				var nslookup = new LookupClient();
-				using var cts = new CancellationTokenSource(100);
-				var query = await nslookup.QueryAsync(s.Split('@')[1], QueryType.MX, cancellationToken: cts.Token);
-				var result = await query.Answers.MxRecords().SelectAsync(r => Dns.GetHostAddressesAsync(r.Exchange.Value).ContinueWith(t =>
-				{
-					if (t.IsCanceled || t.IsFaulted)
-					{
-						return new[] { IPAddress.Loopback };
-					}
-
-					return t.Result;
-				}));
-				isMatch = result.SelectMany(a => a).Any(ip => !ip.IsPrivateIP());
-			}
-
-			return (isMatch, match);
-		}
-
-		/// <summary>
-		/// 邮箱掩码
-		/// </summary>
-		/// <param name="s">邮箱</param>
-		/// <param name="mask">掩码</param>
-		/// <returns></returns>
-		public static string MaskEmail(this string s, char mask = '*')
-		{
-			var index = s.LastIndexOf("@");
-			var oldValue = s.Substring(0, index);
-			return !MatchEmail(s).isMatch ? s : s.Replace(oldValue, Mask(oldValue, mask));
-		}
-
-		#endregion Email
-
-		#region 匹配完整的URL
-
-		/// <summary>
-		/// 匹配完整格式的URL
-		/// </summary>
-		/// <param name="s">源字符串</param>
-		/// <param name="isMatch">是否匹配成功,若返回true,则会得到一个Match对象,否则为null</param>
-		/// <returns>匹配对象</returns>
-		public static Uri MatchUrl(this string s, out bool isMatch)
-		{
-			try
-			{
-				var uri = new Uri(s);
-				isMatch = Dns.GetHostAddresses(uri.Host).Any(ip => !ip.IsPrivateIP());
-				return uri;
-			}
-			catch
-			{
-				isMatch = false;
-				return null;
-			}
-		}
-
-		/// <summary>
-		/// 匹配完整格式的URL
-		/// </summary>
-		/// <param name="s">源字符串</param>
-		/// <returns>是否匹配成功</returns>
-		public static bool MatchUrl(this string s)
-		{
-			MatchUrl(s, out var isMatch);
-			return isMatch;
-		}
-
-		#endregion 匹配完整的URL
-
-		#region 权威校验身份证号码
-
-		/// <summary>
-		/// 根据GB11643-1999标准权威校验中国身份证号码的合法性
-		/// </summary>
-		/// <param name="s">源字符串</param>
-		/// <returns>是否匹配成功</returns>
-		public static bool MatchIdentifyCard(this string s)
-		{
-			return s.Length switch
-			{
-				18 => CheckChinaID18(s),
-				15 => CheckChinaID15(s),
-				_ => false
-			};
-		}
-
-		private static readonly string[] ChinaIDProvinceCodes = {
-			 "11", "12", "13", "14", "15",
-			 "21", "22", "23",
-			 "31", "32", "33", "34", "35", "36", "37",
-			 "41", "42", "43", "44", "45", "46",
-			 "50", "51", "52", "53", "54",
-			 "61", "62", "63", "64", "65",
-			 "71",
-			 "81", "82",
-			 "91"
-		};
-
-		private static bool CheckChinaID18(string ID)
-		{
-			ID = ID.ToUpper();
-			Match m = Regex.Match(ID, @"\d{17}[\dX]", RegexOptions.IgnoreCase);
-			if (!m.Success)
-			{
-				return false;
-			}
-			if (!ChinaIDProvinceCodes.Contains(ID.Substring(0, 2)))
-			{
-				return false;
-			}
-			CultureInfo zhCN = new CultureInfo("zh-CN", true);
-			if (!DateTime.TryParseExact(ID.Substring(6, 8), "yyyyMMdd", zhCN, DateTimeStyles.None, out DateTime birthday))
-			{
-				return false;
-			}
-			if (!birthday.In(new DateTime(1800, 1, 1), DateTime.Today))
-			{
-				return false;
-			}
-			int[] factors = { 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++)
-			{
-				sum += (ID[i] - '0') * factors[i];
-			}
-			int n = (12 - sum % 11) % 11;
-			return n < 10 ? ID[17] - '0' == n : ID[17].Equals('X');
-		}
-
-		private static bool CheckChinaID15(string ID)
-		{
-			Match m = Regex.Match(ID, @"\d{15}", RegexOptions.IgnoreCase);
-			if (!m.Success)
-			{
-				return false;
-			}
-			if (!ChinaIDProvinceCodes.Contains(ID.Substring(0, 2)))
-			{
-				return false;
-			}
-			CultureInfo zhCN = new CultureInfo("zh-CN", true);
-			if (!DateTime.TryParseExact("19" + ID.Substring(6, 6), "yyyyMMdd", zhCN, DateTimeStyles.None, out DateTime birthday))
-			{
-				return false;
-			}
-			return birthday.In(new DateTime(1800, 1, 1), new DateTime(2000, 1, 1));
-		}
-
-		#endregion 权威校验身份证号码
-
-		#region IP地址
-
-		/// <summary>
-		/// 校验IP地址的正确性,同时支持IPv4和IPv6
-		/// </summary>
-		/// <param name="s">源字符串</param>
-		/// <param name="isMatch">是否匹配成功,若返回true,则会得到一个Match对象,否则为null</param>
-		/// <returns>匹配对象</returns>
-		public static IPAddress MatchInetAddress(this string s, out bool isMatch)
-		{
-			isMatch = IPAddress.TryParse(s, out var ip);
-			return ip;
-		}
-
-		/// <summary>
-		/// 校验IP地址的正确性,同时支持IPv4和IPv6
-		/// </summary>
-		/// <param name="s">源字符串</param>
-		/// <returns>是否匹配成功</returns>
-		public static bool MatchInetAddress(this string s)
-		{
-			MatchInetAddress(s, out var success);
-			return success;
-		}
-
-		/// <summary>
-		/// IP地址转换成数字
-		/// </summary>
-		/// <param name="addr">IP地址</param>
-		/// <returns>数字,输入无效IP地址返回0</returns>
-		public static uint IPToID(this string addr)
-		{
-			if (!IPAddress.TryParse(addr, out var ip))
-			{
-				return 0;
-			}
-
-			byte[] bInt = ip.GetAddressBytes();
-			if (BitConverter.IsLittleEndian)
-			{
-				Array.Reverse(bInt);
-			}
-
-			return BitConverter.ToUInt32(bInt, 0);
-		}
-
-		/// <summary>
-		/// IP地址转换成数字
-		/// </summary>
-		/// <param name="ip">IP地址</param>
-		/// <returns>数字,输入无效IP地址返回0</returns>
-		public static uint ToUInt32(this IPAddress ip)
-		{
-			byte[] bInt = ip.GetAddressBytes();
-			if (BitConverter.IsLittleEndian)
-			{
-				Array.Reverse(bInt);
-			}
-
-			return BitConverter.ToUInt32(bInt, 0);
-		}
-
-		/// <summary>
-		/// 判断IP是否是私有地址
-		/// </summary>
-		/// <param name="ip"></param>
-		/// <returns></returns>
-		public static bool IsPrivateIP(this string ip)
-		{
-			var address = MatchInetAddress(ip, out var b);
-			return b && address.IsPrivateIP();
-		}
-
-		/// <summary>
-		/// 判断IP地址在不在某个IP地址段
-		/// </summary>
-		/// <param name="input">需要判断的IP地址</param>
-		/// <param name="begin">起始地址</param>
-		/// <param name="ends">结束地址</param>
-		/// <returns></returns>
-		public static bool IpAddressInRange(this string input, string begin, string ends)
-		{
-			uint current = input.IPToID();
-			return current >= begin.IPToID() && current <= ends.IPToID();
-		}
-
-		/// <summary>
-		/// 判断IP地址在不在某个IP地址段
-		/// </summary>
-		/// <param name="input">需要判断的IP地址</param>
-		/// <param name="begin">起始地址</param>
-		/// <param name="ends">结束地址</param>
-		/// <returns></returns>
-		public static bool IpAddressInRange(this IPAddress input, IPAddress begin, IPAddress ends)
-		{
-			uint current = input.ToUInt32();
-			return current >= begin.ToUInt32() && current <= ends.ToUInt32();
-		}
-
-		#endregion IP地址
-
-		#region 校验手机号码的正确性
-
-		/// <summary>
-		/// 匹配手机号码
-		/// </summary>
-		/// <param name="s">源字符串</param>
-		/// <returns>是否匹配成功</returns>
-		public static bool MatchPhoneNumber(this string s)
-		{
-			return !string.IsNullOrEmpty(s) && s.Length == 11 && s[0] == '1' && (s[1] > '2' || s[1] <= '9');
-		}
-
-		/// <summary>
-		/// 匹配固话号码
-		/// </summary>
-		/// <param name="s">源字符串</param>
-		/// <returns>是否匹配成功</returns>
-		public static bool MatchLandline(this string s)
-		{
-			return Regex.IsMatch(s, @"^0\d{2,3}(?:-?\d{8}|-?\d{7})$");
-		}
-
-		#endregion 校验手机号码的正确性
-
-		#region Url
-
-		/// <summary>
-		/// 判断url是否是外部地址
-		/// </summary>
-		/// <param name="url"></param>
-		/// <returns></returns>
-		public static bool IsExternalAddress(this string url)
-		{
-			var uri = new Uri(url);
-			switch (uri.HostNameType)
-			{
-				case UriHostNameType.Dns:
-					var ipHostEntry = Dns.GetHostEntry(uri.DnsSafeHost);
-					if (ipHostEntry.AddressList.Where(ipAddress => ipAddress.AddressFamily == AddressFamily.InterNetwork).Any(ipAddress => !ipAddress.IsPrivateIP()))
-					{
-						return true;
-					}
-					break;
-
-				case UriHostNameType.IPv4:
-					return !IPAddress.Parse(uri.DnsSafeHost).IsPrivateIP();
-			}
-			return false;
-		}
-
-		#endregion Url
-
-		/// <summary>
-		/// 转换成字节数组
-		/// </summary>
-		/// <param name="this"></param>
-		/// <returns></returns>
-		public static byte[] ToByteArray(this string @this)
-		{
-			return Encoding.UTF8.GetBytes(@this);
-		}
-
-		#region Crc32
-
-		/// <summary>
-		/// 获取字符串crc32签名
-		/// </summary>
-		/// <param name="s"></param>
-		/// <returns></returns>
-		public static string Crc32(this string s)
-		{
-			return string.Join(string.Empty, new Security.Crc32().ComputeHash(Encoding.UTF8.GetBytes(s)).Select(b => b.ToString("x2")));
-		}
-
-		/// <summary>
-		/// 获取字符串crc64签名
-		/// </summary>
-		/// <param name="s"></param>
-		/// <returns></returns>
-		public static string Crc64(this string s)
-		{
-			return string.Join(string.Empty, new Security.Crc64().ComputeHash(Encoding.UTF8.GetBytes(s)).Select(b => b.ToString("x2")));
-		}
-
-		#endregion Crc32
-
-		#region 权威校验中国专利申请号/专利号
-
-		/// <summary>
-		/// 中国专利申请号(授权以后就是专利号)由两种组成
-		/// 2003年9月30号以前的9位(不带校验位是8号),校验位之前可能还会有一个点,例如:00262311, 002623110 或 00262311.0
-		/// 2003年10月1号以后的13位(不带校验位是12号),校验位之前可能还会有一个点,例如:200410018477, 2004100184779 或200410018477.9
-		/// http://www.sipo.gov.cn/docs/pub/old/wxfw/zlwxxxggfw/hlwzljsxt/hlwzljsxtsyzn/201507/P020150713610193194682.pdf
-		/// 上面的文档中均不包括校验算法,但是下面的校验算法没有问题
-		/// </summary>
-		/// <param name="patnum">源字符串</param>
-		/// <returns>是否匹配成功</returns>
-		public static bool MatchCNPatentNumber(this string patnum)
-		{
-			Regex patnumWithCheckbitPattern = new Regex(@"^
+    public static partial class StringExtensions
+    {
+        public static string Join(this IEnumerable<string> strs, string separate = ", ", bool removeEmptyEntry = false) => string.Join(separate, removeEmptyEntry ? strs.Where(s => !string.IsNullOrEmpty(s)) : strs);
+
+        public static string Join<T>(this IEnumerable<T> strs, string separate = ", ", bool removeEmptyEntry = false) => string.Join(separate, removeEmptyEntry ? strs.Where(t => t != null) : strs);
+
+        /// <summary>
+        /// 字符串转时间
+        /// </summary>
+        /// <param name="value"></param>
+        /// <returns></returns>
+        public static DateTime ToDateTime(this string value)
+        {
+            DateTime.TryParse(value, out var result);
+            return result;
+        }
+
+        /// <summary>
+        /// 字符串转Guid
+        /// </summary>
+        /// <param name="s"></param>
+        /// <returns></returns>
+        public static Guid ToGuid(this string s)
+        {
+            return Guid.Parse(s);
+        }
+
+        /// <summary>
+        /// 根据正则替换
+        /// </summary>
+        /// <param name="input"></param>
+        /// <param name="regex">正则表达式</param>
+        /// <param name="replacement">新内容</param>
+        /// <returns></returns>
+        public static string Replace(this string input, Regex regex, string replacement)
+        {
+            return regex.Replace(input, replacement);
+        }
+
+        /// <summary>
+        /// 生成唯一短字符串
+        /// </summary>
+        /// <param name="str"></param>
+        /// <param name="chars">可用字符数数量,0-9,a-z,A-Z</param>
+        /// <returns></returns>
+        public static string CreateShortToken(this string str, byte chars = 36)
+        {
+            var nf = new NumberFormater(chars);
+            return nf.ToString((DateTime.Now.Ticks - 630822816000000000) * 100 + Stopwatch.GetTimestamp() % 100);
+        }
+
+        /// <summary>
+        /// 任意进制转十进制
+        /// </summary>
+        /// <param name="str"></param>
+        /// <param name="base">进制</param>
+        /// <returns></returns>
+        public static long FromBase(this string str, byte @base)
+        {
+            var nf = new NumberFormater(@base);
+            return nf.FromString(str);
+        }
+
+        /// <summary>
+        /// 任意进制转大数十进制
+        /// </summary>
+        /// <param name="str"></param>
+        /// <param name="base">进制</param>
+        /// <returns></returns>
+        public static BigInteger FromBaseBig(this string str, byte @base)
+        {
+            var nf = new NumberFormater(@base);
+            return nf.FromStringBig(str);
+        }
+
+        #region 检测字符串中是否包含列表中的关键词
+
+        /// <summary>
+        /// 检测字符串中是否包含列表中的关键词(快速匹配)
+        /// </summary>
+        /// <param name="s">源字符串</param>
+        /// <param name="keys">关键词列表</param>
+        /// <param name="ignoreCase">忽略大小写</param>
+        /// <returns></returns>
+        public static bool Contains(this string s, IEnumerable<string> keys, bool ignoreCase = true)
+        {
+            if (keys is not ICollection<string> array)
+            {
+                array = keys.ToArray();
+            }
+
+            if (array.Count == 0 || string.IsNullOrEmpty(s))
+            {
+                return false;
+            }
+
+            return ignoreCase ? array.Any(item => s.IndexOf(item, StringComparison.InvariantCultureIgnoreCase) >= 0) : array.Any(s.Contains);
+        }
+
+        /// <summary>
+        /// 检测字符串中是否包含列表中的关键词(安全匹配)
+        /// </summary>
+        /// <param name="s">源字符串</param>
+        /// <param name="keys">关键词列表</param>
+        /// <param name="ignoreCase">忽略大小写</param>
+        /// <returns></returns>
+        public static bool ContainsSafety(this string s, IEnumerable<string> keys, bool ignoreCase = true)
+        {
+            if (keys is not ICollection<string> array)
+            {
+                array = keys.ToArray();
+            }
+
+            if (array.Count == 0 || string.IsNullOrEmpty(s))
+            {
+                return false;
+            }
+
+            bool flag = false;
+            if (ignoreCase)
+            {
+                foreach (var item in array)
+                {
+                    if (s.Contains(item))
+                    {
+                        flag = true;
+                    }
+                }
+            }
+            else
+            {
+                foreach (var item in array)
+                {
+                    if (s.IndexOf(item, StringComparison.InvariantCultureIgnoreCase) >= 0)
+                    {
+                        flag = true;
+                    }
+                }
+            }
+
+            return flag;
+        }
+
+        /// <summary>
+        /// 检测字符串中是否以列表中的关键词结尾
+        /// </summary>
+        /// <param name="s">源字符串</param>
+        /// <param name="keys">关键词列表</param>
+        /// <param name="ignoreCase">忽略大小写</param>
+        /// <returns></returns>
+        public static bool EndsWith(this string s, IEnumerable<string> keys, bool ignoreCase = true)
+        {
+            if (keys is not ICollection<string> array)
+            {
+                array = keys.ToArray();
+            }
+
+            if (array.Count == 0 || string.IsNullOrEmpty(s))
+            {
+                return false;
+            }
+
+            var pattern = $"({array.Select(Regex.Escape).Join("|")})$";
+            return ignoreCase ? Regex.IsMatch(s, pattern, RegexOptions.IgnoreCase) : Regex.IsMatch(s, pattern);
+        }
+
+        /// <summary>
+        /// 检测字符串中是否以列表中的关键词开始
+        /// </summary>
+        /// <param name="s">源字符串</param>
+        /// <param name="keys">关键词列表</param>
+        /// <param name="ignoreCase">忽略大小写</param>
+        /// <returns></returns>
+        public static bool StartsWith(this string s, IEnumerable<string> keys, bool ignoreCase = true)
+        {
+            if (keys is not ICollection<string> array)
+            {
+                array = keys.ToArray();
+            }
+
+            if (array.Count == 0 || string.IsNullOrEmpty(s))
+            {
+                return false;
+            }
+
+            var pattern = $"^({array.Select(Regex.Escape).Join("|")})";
+            return ignoreCase ? Regex.IsMatch(s, pattern, RegexOptions.IgnoreCase) : Regex.IsMatch(s, pattern);
+        }
+
+        /// <summary>
+        /// 检测字符串中是否包含列表中的关键词
+        /// </summary>
+        /// <param name="s">源字符串</param>
+        /// <param name="regex">关键词列表</param>
+        /// <param name="ignoreCase">忽略大小写</param>
+        /// <returns></returns>
+        public static bool RegexMatch(this string s, string regex, bool ignoreCase = true)
+        {
+            if (string.IsNullOrEmpty(regex) || string.IsNullOrEmpty(s))
+            {
+                return false;
+            }
+
+            if (ignoreCase)
+            {
+                return Regex.IsMatch(s, regex, RegexOptions.IgnoreCase);
+            }
+
+            return Regex.IsMatch(s, regex);
+        }
+
+        /// <summary>
+        /// 检测字符串中是否包含列表中的关键词
+        /// </summary>
+        /// <param name="s">源字符串</param>
+        /// <param name="regex">关键词列表</param>
+        /// <returns></returns>
+        public static bool RegexMatch(this string s, Regex regex) => !string.IsNullOrEmpty(s) && regex.IsMatch(s);
+
+        #endregion 检测字符串中是否包含列表中的关键词
+
+        /// <summary>
+        /// 判断字符串是否为空或""
+        /// </summary>
+        /// <param name="s"></param>
+        /// <returns></returns>
+        public static bool IsNullOrEmpty(this string s)
+        {
+            return string.IsNullOrWhiteSpace(s) || s.Equals("null", StringComparison.CurrentCultureIgnoreCase);
+        }
+
+        /// <summary>
+        /// 判断字符串不为空或""
+        /// </summary>
+        /// <param name="s"></param>
+        /// <returns></returns>
+        public static bool NotNullOrEmpty(this string s)
+        {
+            return !string.IsNullOrWhiteSpace(s) && !s.Equals("null", StringComparison.CurrentCultureIgnoreCase);
+        }
+
+        /// <summary>
+        /// 转成非null
+        /// </summary>
+        /// <param name="s"></param>
+        /// <returns></returns>
+        public static string AsNotNull(this string s)
+        {
+            return string.IsNullOrEmpty(s) ? "" : s;
+        }
+
+        /// <summary>
+        /// 转成非null
+        /// </summary>
+        /// <param name="s"></param>
+        /// <param name="value">为空时的替换值</param>
+        /// <returns></returns>
+        public static string IfNullOrEmpty(this string s, string value)
+        {
+            return string.IsNullOrEmpty(s) ? value : s;
+        }
+
+        /// <summary>
+        /// 转成非null
+        /// </summary>
+        /// <param name="s"></param>
+        /// <param name="valueFactory">为空时的替换值函数</param>
+        /// <returns></returns>
+        public static string IfNullOrEmpty(this string s, Func<string> valueFactory)
+        {
+            return string.IsNullOrEmpty(s) ? valueFactory() : s;
+        }
+
+        /// <summary>
+        /// 字符串掩码
+        /// </summary>
+        /// <param name="s">字符串</param>
+        /// <param name="mask">掩码符</param>
+        /// <returns></returns>
+        public static string Mask(this string s, char mask = '*')
+        {
+            if (string.IsNullOrWhiteSpace(s?.Trim()))
+            {
+                return s;
+            }
+
+            s = s.Trim();
+            string masks = mask.ToString().PadLeft(4, mask);
+            return s.Length switch
+            {
+                >= 11 => Regex.Replace(s, "(.{3}).*(.{4})", $"$1{masks}$2"),
+                10 => Regex.Replace(s, "(.{3}).*(.{3})", $"$1{masks}$2"),
+                9 => Regex.Replace(s, "(.{2}).*(.{3})", $"$1{masks}$2"),
+                8 => Regex.Replace(s, "(.{2}).*(.{2})", $"$1{masks}$2"),
+                7 => Regex.Replace(s, "(.{1}).*(.{2})", $"$1{masks}$2"),
+                6 => Regex.Replace(s, "(.{1}).*(.{1})", $"$1{masks}$2"),
+                _ => Regex.Replace(s, "(.{1}).*", $"$1{masks}")
+            };
+        }
+
+        #region Email
+
+        /// <summary>
+        /// 匹配Email
+        /// </summary>
+        /// <param name="s">源字符串</param>
+        /// <param name="valid">是否验证有效性</param>
+        /// <returns>匹配对象;是否匹配成功,若返回true,则会得到一个Match对象,否则为null</returns>
+        public static (bool isMatch, Match match) MatchEmail(this string s, bool valid = false)
+        {
+            if (string.IsNullOrEmpty(s) || s.Length < 7)
+            {
+                return (false, null);
+            }
+
+            var match = Regex.Match(s, @"[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n]+");
+            var isMatch = match.Success;
+            if (isMatch && valid)
+            {
+                var nslookup = new LookupClient();
+                var task = nslookup.Query(s.Split('@')[1], QueryType.MX).Answers.MxRecords().SelectAsync(r => Dns.GetHostAddressesAsync(r.Exchange.Value).ContinueWith(t =>
+                {
+                    if (t.IsCanceled || t.IsFaulted)
+                    {
+                        return new[]
+                        {
+                            IPAddress.Loopback
+                        };
+                    }
+
+                    return t.Result;
+                }));
+                isMatch = task.Result.SelectMany(a => a).Any(ip => !ip.IsPrivateIP());
+            }
+
+            return (isMatch, match);
+        }
+
+        /// <summary>
+        /// 匹配Email
+        /// </summary>
+        /// <param name="s">源字符串</param>
+        /// <param name="valid">是否验证有效性</param>
+        /// <returns>匹配对象;是否匹配成功,若返回true,则会得到一个Match对象,否则为null</returns>
+        public static async Task<(bool isMatch, Match match)> MatchEmailAsync(this string s, bool valid = false)
+        {
+            if (string.IsNullOrEmpty(s) || s.Length < 7)
+            {
+                return (false, null);
+            }
+
+            var match = Regex.Match(s, @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");
+            var isMatch = match.Success;
+            if (isMatch && valid)
+            {
+                var nslookup = new LookupClient();
+                using var cts = new CancellationTokenSource(100);
+                var query = await nslookup.QueryAsync(s.Split('@')[1], QueryType.MX, cancellationToken: cts.Token);
+                var result = await query.Answers.MxRecords().SelectAsync(r => Dns.GetHostAddressesAsync(r.Exchange.Value).ContinueWith(t =>
+                {
+                    if (t.IsCanceled || t.IsFaulted)
+                    {
+                        return new[] { IPAddress.Loopback };
+                    }
+
+                    return t.Result;
+                }));
+                isMatch = result.SelectMany(a => a).Any(ip => !ip.IsPrivateIP());
+            }
+
+            return (isMatch, match);
+        }
+
+        /// <summary>
+        /// 邮箱掩码
+        /// </summary>
+        /// <param name="s">邮箱</param>
+        /// <param name="mask">掩码</param>
+        /// <returns></returns>
+        public static string MaskEmail(this string s, char mask = '*')
+        {
+            var index = s.LastIndexOf("@");
+            var oldValue = s.Substring(0, index);
+            return !MatchEmail(s).isMatch ? s : s.Replace(oldValue, Mask(oldValue, mask));
+        }
+
+        #endregion Email
+
+        #region 匹配完整的URL
+
+        /// <summary>
+        /// 匹配完整格式的URL
+        /// </summary>
+        /// <param name="s">源字符串</param>
+        /// <param name="isMatch">是否匹配成功,若返回true,则会得到一个Match对象,否则为null</param>
+        /// <returns>匹配对象</returns>
+        public static Uri MatchUrl(this string s, out bool isMatch)
+        {
+            try
+            {
+                var uri = new Uri(s);
+                isMatch = Dns.GetHostAddresses(uri.Host).Any(ip => !ip.IsPrivateIP());
+                return uri;
+            }
+            catch
+            {
+                isMatch = false;
+                return null;
+            }
+        }
+
+        /// <summary>
+        /// 匹配完整格式的URL
+        /// </summary>
+        /// <param name="s">源字符串</param>
+        /// <returns>是否匹配成功</returns>
+        public static bool MatchUrl(this string s)
+        {
+            MatchUrl(s, out var isMatch);
+            return isMatch;
+        }
+
+        #endregion 匹配完整的URL
+
+        #region 权威校验身份证号码
+
+        /// <summary>
+        /// 根据GB11643-1999标准权威校验中国身份证号码的合法性
+        /// </summary>
+        /// <param name="s">源字符串</param>
+        /// <returns>是否匹配成功</returns>
+        public static bool MatchIdentifyCard(this string s)
+        {
+            return s.Length switch
+            {
+                18 => CheckChinaID18(s),
+                15 => CheckChinaID15(s),
+                _ => false
+            };
+        }
+
+        private static readonly string[] ChinaIDProvinceCodes = {
+             "11", "12", "13", "14", "15",
+             "21", "22", "23",
+             "31", "32", "33", "34", "35", "36", "37",
+             "41", "42", "43", "44", "45", "46",
+             "50", "51", "52", "53", "54",
+             "61", "62", "63", "64", "65",
+            "71",
+            "81",
+            "82",
+            "91"
+        };
+
+        private static bool CheckChinaID18(string ID)
+        {
+            ID = ID.ToUpper();
+            Match m = Regex.Match(ID, @"\d{17}[\dX]", RegexOptions.IgnoreCase);
+            if (!m.Success)
+            {
+                return false;
+            }
+
+            if (!ChinaIDProvinceCodes.Contains(ID.Substring(0, 2)))
+            {
+                return false;
+            }
+
+            CultureInfo zhCN = new CultureInfo("zh-CN", true);
+            if (!DateTime.TryParseExact(ID.Substring(6, 8), "yyyyMMdd", zhCN, DateTimeStyles.None, out DateTime birthday))
+            {
+                return false;
+            }
+
+            if (!birthday.In(new DateTime(1800, 1, 1), DateTime.Today))
+            {
+                return false;
+            }
+            int[] factors = { 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++)
+            {
+                sum += (ID[i] - '0') * factors[i];
+            }
+
+            int n = (12 - sum % 11) % 11;
+            return n < 10 ? ID[17] - '0' == n : ID[17].Equals('X');
+        }
+
+        private static bool CheckChinaID15(string ID)
+        {
+            Match m = Regex.Match(ID, @"\d{15}", RegexOptions.IgnoreCase);
+            if (!m.Success)
+            {
+                return false;
+            }
+
+            if (!ChinaIDProvinceCodes.Contains(ID.Substring(0, 2)))
+            {
+                return false;
+            }
+
+            CultureInfo zhCN = new CultureInfo("zh-CN", true);
+            if (!DateTime.TryParseExact("19" + ID.Substring(6, 6), "yyyyMMdd", zhCN, DateTimeStyles.None, out DateTime birthday))
+            {
+                return false;
+            }
+
+            return birthday.In(new DateTime(1800, 1, 1), new DateTime(2000, 1, 1));
+        }
+
+        #endregion 权威校验身份证号码
+
+        #region IP地址
+
+        /// <summary>
+        /// 校验IP地址的正确性,同时支持IPv4和IPv6
+        /// </summary>
+        /// <param name="s">源字符串</param>
+        /// <param name="isMatch">是否匹配成功,若返回true,则会得到一个Match对象,否则为null</param>
+        /// <returns>匹配对象</returns>
+        public static IPAddress MatchInetAddress(this string s, out bool isMatch)
+        {
+            isMatch = IPAddress.TryParse(s, out var ip);
+            return ip;
+        }
+
+        /// <summary>
+        /// 校验IP地址的正确性,同时支持IPv4和IPv6
+        /// </summary>
+        /// <param name="s">源字符串</param>
+        /// <returns>是否匹配成功</returns>
+        public static bool MatchInetAddress(this string s)
+        {
+            MatchInetAddress(s, out var success);
+            return success;
+        }
+
+        /// <summary>
+        /// IP地址转换成数字
+        /// </summary>
+        /// <param name="addr">IP地址</param>
+        /// <returns>数字,输入无效IP地址返回0</returns>
+        public static uint IPToID(this string addr)
+        {
+            if (!IPAddress.TryParse(addr, out var ip))
+            {
+                return 0;
+            }
+
+            byte[] bInt = ip.GetAddressBytes();
+            if (BitConverter.IsLittleEndian)
+            {
+                Array.Reverse(bInt);
+            }
+
+            return BitConverter.ToUInt32(bInt, 0);
+        }
+
+        /// <summary>
+        /// IP地址转换成数字
+        /// </summary>
+        /// <param name="ip">IP地址</param>
+        /// <returns>数字,输入无效IP地址返回0</returns>
+        public static uint ToUInt32(this IPAddress ip)
+        {
+            byte[] bInt = ip.GetAddressBytes();
+            if (BitConverter.IsLittleEndian)
+            {
+                Array.Reverse(bInt);
+            }
+
+            return BitConverter.ToUInt32(bInt, 0);
+        }
+
+        /// <summary>
+        /// 判断IP是否是私有地址
+        /// </summary>
+        /// <param name="ip"></param>
+        /// <returns></returns>
+        public static bool IsPrivateIP(this string ip)
+        {
+            var address = MatchInetAddress(ip, out var b);
+            return b && address.IsPrivateIP();
+        }
+
+        /// <summary>
+        /// 判断IP地址在不在某个IP地址段
+        /// </summary>
+        /// <param name="input">需要判断的IP地址</param>
+        /// <param name="begin">起始地址</param>
+        /// <param name="ends">结束地址</param>
+        /// <returns></returns>
+        public static bool IpAddressInRange(this string input, string begin, string ends)
+        {
+            uint current = input.IPToID();
+            return current >= begin.IPToID() && current <= ends.IPToID();
+        }
+
+        /// <summary>
+        /// 判断IP地址在不在某个IP地址段
+        /// </summary>
+        /// <param name="input">需要判断的IP地址</param>
+        /// <param name="begin">起始地址</param>
+        /// <param name="ends">结束地址</param>
+        /// <returns></returns>
+        public static bool IpAddressInRange(this IPAddress input, IPAddress begin, IPAddress ends)
+        {
+            uint current = input.ToUInt32();
+            return current >= begin.ToUInt32() && current <= ends.ToUInt32();
+        }
+
+        #endregion IP地址
+
+        #region 校验手机号码的正确性
+
+        /// <summary>
+        /// 匹配手机号码
+        /// </summary>
+        /// <param name="s">源字符串</param>
+        /// <returns>是否匹配成功</returns>
+        public static bool MatchPhoneNumber(this string s)
+        {
+            return !string.IsNullOrEmpty(s) && s.Length == 11 && s[0] == '1' && (s[1] > '2' || s[1] <= '9');
+        }
+
+        /// <summary>
+        /// 匹配固话号码
+        /// </summary>
+        /// <param name="s">源字符串</param>
+        /// <returns>是否匹配成功</returns>
+        public static bool MatchLandline(this string s)
+        {
+            return Regex.IsMatch(s, @"^0\d{2,3}(?:-?\d{8}|-?\d{7})$");
+        }
+
+        #endregion 校验手机号码的正确性
+
+        #region Url
+
+        /// <summary>
+        /// 判断url是否是外部地址
+        /// </summary>
+        /// <param name="url"></param>
+        /// <returns></returns>
+        public static bool IsExternalAddress(this string url)
+        {
+            var uri = new Uri(url);
+            switch (uri.HostNameType)
+            {
+                case UriHostNameType.Dns:
+                    var ipHostEntry = Dns.GetHostEntry(uri.DnsSafeHost);
+                    if (ipHostEntry.AddressList.Where(ipAddress => ipAddress.AddressFamily == AddressFamily.InterNetwork).Any(ipAddress => !ipAddress.IsPrivateIP()))
+                    {
+                        return true;
+                    }
+
+                    break;
+
+                case UriHostNameType.IPv4:
+                    return !IPAddress.Parse(uri.DnsSafeHost).IsPrivateIP();
+            }
+
+            return false;
+        }
+
+        #endregion Url
+
+        /// <summary>
+        /// 转换成字节数组
+        /// </summary>
+        /// <param name="this"></param>
+        /// <returns></returns>
+        public static byte[] ToByteArray(this string @this)
+        {
+            return Encoding.UTF8.GetBytes(@this);
+        }
+
+        #region Crc32
+
+        /// <summary>
+        /// 获取字符串crc32签名
+        /// </summary>
+        /// <param name="s"></param>
+        /// <returns></returns>
+        public static string Crc32(this string s)
+        {
+            return string.Join(string.Empty, new Security.Crc32().ComputeHash(Encoding.UTF8.GetBytes(s)).Select(b => b.ToString("x2")));
+        }
+
+        /// <summary>
+        /// 获取字符串crc64签名
+        /// </summary>
+        /// <param name="s"></param>
+        /// <returns></returns>
+        public static string Crc64(this string s)
+        {
+            return string.Join(string.Empty, new Security.Crc64().ComputeHash(Encoding.UTF8.GetBytes(s)).Select(b => b.ToString("x2")));
+        }
+
+        #endregion Crc32
+
+        #region 权威校验中国专利申请号/专利号
+
+        /// <summary>
+        /// 中国专利申请号(授权以后就是专利号)由两种组成
+        /// 2003年9月30号以前的9位(不带校验位是8号),校验位之前可能还会有一个点,例如:00262311, 002623110 或 00262311.0
+        /// 2003年10月1号以后的13位(不带校验位是12号),校验位之前可能还会有一个点,例如:200410018477, 2004100184779 或200410018477.9
+        /// http://www.sipo.gov.cn/docs/pub/old/wxfw/zlwxxxggfw/hlwzljsxt/hlwzljsxtsyzn/201507/P020150713610193194682.pdf
+        /// 上面的文档中均不包括校验算法,但是下面的校验算法没有问题
+        /// </summary>
+        /// <param name="patnum">源字符串</param>
+        /// <returns>是否匹配成功</returns>
+        public static bool MatchCNPatentNumber(this string patnum)
+        {
+            Regex patnumWithCheckbitPattern = new Regex(@"^
 (?<!\d)
 (?<patentnum>
-	(?<basenum>
-		(?<year>(?<old>8[5-9]|9[0-9]|0[0-3])|(?<new>[2-9]\d{3}))
-		(?<sn>
-			(?<patenttype>[12389])
-			(?(old)\d{5}|(?(new)\d{7}))
-		)
-	)
-	(?:
-	\.?
-	(?<checkbit>[0-9X])
-	)?
+    (?<basenum>
+        (?<year>(?<old>8[5-9]|9[0-9]|0[0-3])|(?<new>[2-9]\d{3}))
+        (?<sn>
+            (?<patenttype>[12389])
+            (?(old)\d{5}|(?(new)\d{7}))
+        )
+    )
+    (?:
+    \.?
+    (?<checkbit>[0-9X])
+    )?
 )
 (?!\d)
 $", RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase | RegexOptions.Multiline);
-			Match m = patnumWithCheckbitPattern.Match(patnum);
-			if (!m.Success)
-			{
-				return false;
-			}
-			bool isPatnumTrue = true;
-			patnum = patnum.ToUpper().Replace(".", "");
-			if (patnum.Length == 9 || patnum.Length == 8)
-			{
-				byte[] factors8 = new byte[] { 2, 3, 4, 5, 6, 7, 8, 9 };
-				int year = Convert.ToUInt16(patnum.Substring(0, 2));
-				year += (year >= 85) ? (ushort)1900u : (ushort)2000u;
-				if (year >= 1985 || year <= 2003)
-				{
-					int sum = 0;
-					for (byte i = 0; i < 8; i++)
-					{
-						sum += factors8[i] * (patnum[i] - '0');
-					}
-					char checkbit = "0123456789X"[sum % 11];
-					if (patnum.Length == 9)
-					{
-						if (checkbit != patnum[8])
-						{
-							isPatnumTrue = false;
-						}
-					}
-					else
-					{
-						patnum += checkbit;
-					}
-				}
-				else
-				{
-					isPatnumTrue = false;
-				}
-			}
-			else if (patnum.Length == 13 || patnum.Length == 12)
-			{
-				byte[] factors12 = new byte[12] { 2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5 };
-				int year = Convert.ToUInt16(patnum.Substring(0, 4));
-				if (year >= 2003 && year <= DateTime.Now.Year)
-				{
-					int sum = 0;
-					for (byte i = 0; i < 12; i++)
-					{
-						sum += factors12[i] * (patnum[i] - '0');
-					}
-					char checkbit = "0123456789X"[sum % 11];
-					if (patnum.Length == 13)
-					{
-						if (checkbit != patnum[12])
-						{
-							isPatnumTrue = false;
-						}
-					}
-					else
-					{
-						patnum += checkbit;
-					}
-				}
-				else
-				{
-					isPatnumTrue = false;
-				}
-			}
-			else
-			{
-				isPatnumTrue = false;
-			}
-			return isPatnumTrue;
-		}
-
-		#endregion 权威校验中国专利申请号/专利号
-
-		/// <summary>
-		/// 取字符串前{length}个字
-		/// </summary>
-		/// <param name="s"></param>
-		/// <param name="length"></param>
-		/// <returns></returns>
-		public static string Take(this string s, int length)
-		{
-			return s.Length > length ? s.Substring(0, length) : s;
-		}
-
-		/// <summary>
-		/// 对比字符串的汉明距离
-		/// </summary>
-		/// <param name="this"></param>
-		/// <param name="that"></param>
-		/// <returns></returns>
-		public static int HammingDistance(this string @this, string that) => new SimHash(@this).HammingDistance(new SimHash(that));
-
-		/// <summary>
-		/// 匹配字符串是否包含emoji字符
-		/// </summary>
-		/// <param name="s"></param>
-		/// <returns></returns>
-		public static bool MatchEmoji(this string s)
-		{
-			return Regex.IsMatch(s, @"(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])");
-		}
-
-		/// <summary>
-		/// 获取字符串的字符数
-		/// </summary>
-		/// <param name="str"></param>
-		/// <returns></returns>
-		public static int CharacterCount(this string str)
-		{
-			var enumerator = StringInfo.GetTextElementEnumerator(str);
-			int length = 0;
-			while (enumerator.MoveNext())
-			{
-				length++;
-			}
-			return length;
-		}
-
-		/// <summary>
-		/// 获取字符串的字节数
-		/// </summary>
-		/// <param name="str"></param>
-		/// <returns></returns>
-		public static int BytesCount(this string str)
-		{
-			return Encoding.UTF8.GetByteCount(str);
-		}
-	}
+            Match m = patnumWithCheckbitPattern.Match(patnum);
+            if (!m.Success)
+            {
+                return false;
+            }
+
+            bool isPatnumTrue = true;
+            patnum = patnum.ToUpper().Replace(".", "");
+            if (patnum.Length == 9 || patnum.Length == 8)
+            {
+                byte[] factors8 = new byte[] { 2, 3, 4, 5, 6, 7, 8, 9 };
+                int year = Convert.ToUInt16(patnum.Substring(0, 2));
+                year += (year >= 85) ? (ushort)1900u : (ushort)2000u;
+                if (year >= 1985 || year <= 2003)
+                {
+                    int sum = 0;
+                    for (byte i = 0; i < 8; i++)
+                    {
+                        sum += factors8[i] * (patnum[i] - '0');
+                    }
+
+                    char checkbit = "0123456789X"[sum % 11];
+                    if (patnum.Length == 9)
+                    {
+                        if (checkbit != patnum[8])
+                        {
+                            isPatnumTrue = false;
+                        }
+                    }
+                    else
+                    {
+                        patnum += checkbit;
+                    }
+                }
+                else
+                {
+                    isPatnumTrue = false;
+                }
+            }
+            else if (patnum.Length == 13 || patnum.Length == 12)
+            {
+                byte[] factors12 = new byte[12] { 2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5 };
+                int year = Convert.ToUInt16(patnum.Substring(0, 4));
+                if (year >= 2003 && year <= DateTime.Now.Year)
+                {
+                    int sum = 0;
+                    for (byte i = 0; i < 12; i++)
+                    {
+                        sum += factors12[i] * (patnum[i] - '0');
+                    }
+
+                    char checkbit = "0123456789X"[sum % 11];
+                    if (patnum.Length == 13)
+                    {
+                        if (checkbit != patnum[12])
+                        {
+                            isPatnumTrue = false;
+                        }
+                    }
+                    else
+                    {
+                        patnum += checkbit;
+                    }
+                }
+                else
+                {
+                    isPatnumTrue = false;
+                }
+            }
+            else
+            {
+                isPatnumTrue = false;
+            }
+
+            return isPatnumTrue;
+        }
+
+        #endregion 权威校验中国专利申请号/专利号
+
+        /// <summary>
+        /// 取字符串前{length}个字
+        /// </summary>
+        /// <param name="s"></param>
+        /// <param name="length"></param>
+        /// <returns></returns>
+        public static string Take(this string s, int length)
+        {
+            return s.Length > length ? s.Substring(0, length) : s;
+        }
+
+        /// <summary>
+        /// 对比字符串的汉明距离
+        /// </summary>
+        /// <param name="this"></param>
+        /// <param name="that"></param>
+        /// <returns></returns>
+        public static int HammingDistance(this string @this, string that) => new SimHash(@this).HammingDistance(new SimHash(that));
+
+        /// <summary>
+        /// 匹配字符串是否包含emoji字符
+        /// </summary>
+        /// <param name="s"></param>
+        /// <returns></returns>
+        public static bool MatchEmoji(this string s)
+        {
+            return Regex.IsMatch(s, @"(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])");
+        }
+
+        /// <summary>
+        /// 获取字符串的字符数
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static int CharacterCount(this string str)
+        {
+            var enumerator = StringInfo.GetTextElementEnumerator(str);
+            int length = 0;
+            while (enumerator.MoveNext())
+            {
+                length++;
+            }
+
+            return length;
+        }
+
+        /// <summary>
+        /// 获取字符串的字节数
+        /// </summary>
+        /// <param name="str"></param>
+        /// <returns></returns>
+        public static int BytesCount(this string str)
+        {
+            return Encoding.UTF8.GetByteCount(str);
+        }
+
+        /// <summary>
+        /// 转半角(DBC case)
+        /// </summary>
+        /// <param name="input">任意字符串</param>
+        /// <returns>半角字符串</returns>
+        ///<remarks>
+        ///全角空格为12288,半角空格为32(此处不必转空格)
+        ///其他字符半角(33-126)与全角(65281-65374)的对应关系是:均相差65248
+        ///</remarks>
+        public static string ToDBC(this string input)
+        {
+            char[] c = input.ToCharArray();
+            for (int i = 0; i < c.Length; i++)
+            {
+                if (c[i] == 12288)
+                {
+                    c[i] = (char)32;
+                    continue;
+                }
+
+                if (c[i] > 65280 && c[i] < 65375)
+                {
+                    c[i] = (char)(c[i] - 65248);
+                }
+            }
+
+            return new string(c);
+        }
+
+        /// <summary>
+        /// 转全角(SBC case)
+        /// </summary>
+        /// <param name="input">任意字符串</param>
+        /// <returns>全角字符串</returns>
+        ///<remarks>
+        ///全角空格为12288,半角空格为32
+        ///其他字符半角(33-126)与全角(65281-65374)的对应关系是:均相差65248
+        ///</remarks>
+        public static string ToSBC(this string input)
+        {
+            //半角转全角:
+            var c = input.ToCharArray();
+            for (int i = 0; i < c.Length; i++)
+            {
+                if (c[i] == 32)
+                {
+                    c[i] = (char)12288;
+                    continue;
+                }
+                if (c[i] < 127 && c[i] > 32)
+                {
+                    c[i] = (char)(c[i] + 65248);
+                }
+            }
+            return new string(c);
+        }
+    }
 }

+ 2 - 5
Masuit.Tools/Masuit.Tools.csproj

@@ -9,7 +9,7 @@
     <AppDesignerFolder>Properties</AppDesignerFolder>
     <RootNamespace>Masuit.Tools</RootNamespace>
     <AssemblyName>Masuit.Tools</AssemblyName>
-    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
     <FileAlignment>512</FileAlignment>
     <NuGetPackageImportStamp>
     </NuGetPackageImportStamp>
@@ -199,7 +199,7 @@
   </ItemGroup>
   <ItemGroup>
     <PackageReference Include="Castle.Core">
-      <Version>4.4.1</Version>
+      <Version>5.1.1</Version>
     </PackageReference>
     <PackageReference Include="DnsClient">
       <Version>1.7.0</Version>
@@ -216,9 +216,6 @@
     <PackageReference Include="SharpCompress">
       <Version>0.33.0</Version>
     </PackageReference>
-    <PackageReference Include="SixLabors.ImageSharp">
-      <Version>[2.1.3]</Version>
-    </PackageReference>
     <PackageReference Include="SixLabors.ImageSharp.Drawing">
       <Version>1.0.0</Version>
     </PackageReference>

+ 28 - 28
Masuit.Tools/app.config

@@ -1,61 +1,61 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <configuration>
     <startup>
-        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
     </startup>
     <runtime>
         <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
             <dependentAssembly>
-                <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30AD4FE6B2A6AEED" culture="neutral" />
-                <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
+                <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30AD4FE6B2A6AEED" culture="neutral"/>
+                <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/>
             </dependentAssembly>
             <dependentAssembly>
-                <assemblyIdentity name="AngleSharp" publicKeyToken="e83494dcdc6d31ea" culture="neutral" />
-                <bindingRedirect oldVersion="0.0.0.0-0.13.0.0" newVersion="0.13.0.0" />
+                <assemblyIdentity name="AngleSharp" publicKeyToken="e83494dcdc6d31ea" culture="neutral"/>
+                <bindingRedirect oldVersion="0.0.0.0-0.13.0.0" newVersion="0.13.0.0"/>
             </dependentAssembly>
             <dependentAssembly>
-                <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
-                <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
+                <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
+                <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0"/>
             </dependentAssembly>
             <dependentAssembly>
-                <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
-                <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
+                <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
+                <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0"/>
             </dependentAssembly>
             <dependentAssembly>
-                <assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
-                <bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" />
+                <assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
+                <bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1"/>
             </dependentAssembly>
             <dependentAssembly>
-                <assemblyIdentity name="System.Text.Encoding.CodePages" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
-                <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
+                <assemblyIdentity name="System.Text.Encoding.CodePages" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
+                <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0"/>
             </dependentAssembly>
             <dependentAssembly>
-                <assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
-                <bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" />
+                <assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
+                <bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1"/>
             </dependentAssembly>
             <dependentAssembly>
-                <assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
-                <bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
+                <assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
+                <bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0"/>
             </dependentAssembly>
             <dependentAssembly>
-                <assemblyIdentity name="System.IO.Compression" publicKeyToken="b77a5c561934e089" culture="neutral" />
-                <bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
+                <assemblyIdentity name="System.IO.Compression" publicKeyToken="b77a5c561934e089" culture="neutral"/>
+                <bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0"/>
             </dependentAssembly>
             <dependentAssembly>
-                <assemblyIdentity name="System.Runtime.InteropServices.RuntimeInformation" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
-                <bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
+                <assemblyIdentity name="System.Runtime.InteropServices.RuntimeInformation" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
+                <bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0"/>
             </dependentAssembly>
             <dependentAssembly>
-                <assemblyIdentity name="System.IO.Pipelines" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
-                <bindingRedirect oldVersion="0.0.0.0-5.0.0.1" newVersion="5.0.0.1" />
+                <assemblyIdentity name="System.IO.Pipelines" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
+                <bindingRedirect oldVersion="0.0.0.0-5.0.0.1" newVersion="5.0.0.1"/>
             </dependentAssembly>
             <dependentAssembly>
-                <assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
-                <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
+                <assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
+                <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0"/>
             </dependentAssembly>
             <dependentAssembly>
-                <assemblyIdentity name="System.Threading.Channels" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
-                <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
+                <assemblyIdentity name="System.Threading.Channels" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
+                <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0"/>
             </dependentAssembly>
         </assemblyBinding>
     </runtime>