瀏覽代碼

优化NumberFormater性能

懒得勤快 2 年之前
父節點
當前提交
d495d570ed

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

@@ -16,928 +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);
-        }
-
-        /// <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);
-        }
-    }
+			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);
+		}
+	}
 }

+ 1 - 1
Masuit.Tools.Abstractions/Masuit.Tools.Abstractions.csproj

@@ -3,7 +3,7 @@
         <TargetFrameworks>netstandard2.0;netstandard2.1;net461;net5;net6;net7</TargetFrameworks>
         <LangVersion>latest</LangVersion>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-        <Version>2.6.6.5</Version>
+        <Version>2.6.6.6</Version>
         <Authors>懒得勤快</Authors>
         <Description>新手友好的C#万能工具库,码数吐司库,Masuit.Tools基础公共库(适用于.NET4.6.1/.NET Standard2.0及以上项目),包含一些常用的操作类,大都是静态类,加密解密,反射操作,Excel简单导出,权重随机筛选算法,分布式短id,表达式树,linq扩展,文件压缩,多线程下载和FTP客户端,硬件信息,字符串扩展方法,日期时间扩展操作,中国农历,大文件拷贝,图像裁剪,验证码,断点续传,集合扩展等常用封装。
             官网教程:https://tools.masuit.org

+ 8 - 8
Masuit.Tools.Abstractions/Strings/NumberFormater.cs

@@ -163,22 +163,22 @@ namespace Masuit.Tools.Strings
 			}
 
 			number -= resultOffset;
-			List<string> result = new List<string>();
+			List<char> result = new List<char>();
 			long t = Math.Abs(number);
 			while (t != 0)
 			{
 				var mod = t % Length;
 				t = Math.Abs(t / Length);
-				var character = Characters[Convert.ToInt32(mod) - start].ToString();
+				var character = Characters[Convert.ToInt32(mod) - start];
 				result.Insert(0, character);
 			}
 
 			if (number < 0)
 			{
-				result.Insert(0, "-");
+				result.Insert(0, '-');
 			}
 
-			return string.Join("", result);
+			return new string(result.ToArray());
 		}
 
 		/// <summary>
@@ -202,11 +202,11 @@ namespace Masuit.Tools.Strings
 			}
 
 			number = number - resultOffset;
-			List<string> result = new List<string>();
+			List<char> result = new List<char>();
 			if (number < 0)
 			{
 				number = -number;
-				result.Add("0");
+				result.Add('-');
 			}
 
 			BigInteger t = number;
@@ -215,11 +215,11 @@ namespace Masuit.Tools.Strings
 			{
 				var mod = t % Length;
 				t = BigInteger.Abs(BigInteger.Divide(t, Length));
-				var character = Characters[(int)mod - start].ToString();
+				var character = Characters[(int)mod - start];
 				result.Insert(0, character);
 			}
 
-			return string.Join("", result);
+			return new string(result.ToArray());
 		}
 
 		/// <summary>

+ 281 - 306
Masuit.Tools.Abstractions/Strings/UnicodeFormater.cs

@@ -1,321 +1,296 @@
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Numerics;
 using System.Text;
-using System.Text.RegularExpressions;
 
 namespace Masuit.Tools.Strings
 {
-    /// <summary>
-    /// 数制格式化器
-    /// </summary>
-    public class UnicodeFormater
-    {
-        /// <summary>
-        /// 数制表示字符集
-        /// </summary>
-        private List<string> Characters { get; }
-
-        /// <summary>
-        /// 进制长度
-        /// </summary>
-        public int Length => Characters.Count;
-
-        /// <summary>
-        /// 起始值偏移
-        /// </summary>
-        private readonly byte _offset;
-
-        private readonly bool _emojiMode;
-
-        private static readonly Regex EmojiRegex = new Regex(@"(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])");
-
-        /// <summary>
-        /// 数制格式化器
-        /// </summary>
-        public UnicodeFormater()
-        {
-            Characters = new List<string>() { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
-        }
-
-        /// <summary>
-        /// 数制格式化器
-        /// </summary>
-        /// <param name="characters">符号集</param>
-        /// <param name="offset">起始值偏移</param>
-        public UnicodeFormater(string characters, byte offset = 0)
-        {
-            if (string.IsNullOrEmpty(characters))
-            {
-                throw new ArgumentException("符号集不能为空");
-            }
-
-            var matches = EmojiRegex.Matches(characters);
-            if (matches.Count > 0)
-            {
-                _emojiMode = true;
-                Characters = new List<string>();
-                foreach (Match m in matches)
-                {
-                    Characters.Add(m.Value);
-                }
-            }
-            else
-            {
-                Characters = characters.Select(c => c.ToString()).ToList();
-            }
-
-            _offset = offset;
-        }
-
-        /// <summary>
-        /// 数制格式化器
-        /// </summary>
-        /// <param name="characters">符号集</param>
-        /// <param name="offset">起始值偏移</param>
-        public UnicodeFormater(List<string> characters, byte offset = 0)
-        {
-            if (characters.IsNullOrEmpty())
-            {
-                throw new ArgumentException("符号集不能为空");
-            }
-
-            Characters = characters;
-            _offset = offset;
-        }
+	/// <summary>
+	/// 数制格式化器
+	/// </summary>
+	public class UnicodeFormater
+	{
+		/// <summary>
+		/// 数制表示字符集
+		/// </summary>
+		private List<string> Characters { get; }
+
+		/// <summary>
+		/// 进制长度
+		/// </summary>
+		public int Length => Characters.Count;
+
+		/// <summary>
+		/// 起始值偏移
+		/// </summary>
+		private readonly byte _offset;
+
+		/// <summary>
+		/// 数制格式化器
+		/// </summary>
+		public UnicodeFormater()
+		{
+			Characters = new List<string>() { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+		}
+
+		/// <summary>
+		/// 数制格式化器
+		/// </summary>
+		/// <param name="characters">符号集</param>
+		/// <param name="offset">起始值偏移</param>
+		public UnicodeFormater(string characters, byte offset = 0)
+		{
+			if (string.IsNullOrEmpty(characters))
+			{
+				throw new ArgumentException("符号集不能为空");
+			}
+
+			var enumerator = StringInfo.GetTextElementEnumerator(characters);
+			Characters = new List<string>();
+			while (enumerator.MoveNext())
+			{
+				Characters.Add(enumerator.GetTextElement());
+			}
+
+			_offset = offset;
+		}
+
+		/// <summary>
+		/// 数制格式化器
+		/// </summary>
+		/// <param name="characters">符号集</param>
+		/// <param name="offset">起始值偏移</param>
+		public UnicodeFormater(List<string> characters, byte offset = 0)
+		{
+			if (characters.IsNullOrEmpty())
+			{
+				throw new ArgumentException("符号集不能为空");
+			}
+
+			Characters = characters;
+			_offset = offset;
+		}
 
 #if NET5_0_OR_GREATER
 
-        /// <summary>
-        /// 数制格式化器
-        /// </summary>
-        /// <param name="characters">符号集</param>
-        /// <param name="offset">起始值偏移</param>
-        public UnicodeFormater(ReadOnlySpan<byte> characters, byte offset = 0)
-        {
-            if (characters == null || characters.Length == 0)
-            {
-                throw new ArgumentException("符号集不能为空");
-            }
-
-            Characters = Encoding.UTF8.GetString(characters).Select(c => c.ToString()).ToList();
-            _offset = offset;
-        }
-
-        /// <summary>
-        /// 数制格式化器
-        /// </summary>
-        /// <param name="characters">符号集</param>
-        /// <param name="offset">起始值偏移</param>
-        public UnicodeFormater(ReadOnlySpan<char> characters, byte offset = 0)
-        {
-            if (characters == null || characters.Length == 0)
-            {
-                throw new ArgumentException("符号集不能为空");
-            }
-
-            Characters = new string(characters).Select(c => c.ToString()).ToList();;
-            _offset = offset;
-        }
+		/// <summary>
+		/// 数制格式化器
+		/// </summary>
+		/// <param name="characters">符号集</param>
+		/// <param name="offset">起始值偏移</param>
+		public UnicodeFormater(ReadOnlySpan<byte> characters, byte offset = 0)
+		{
+			if (characters == null || characters.Length == 0)
+			{
+				throw new ArgumentException("符号集不能为空");
+			}
+
+			Characters = Encoding.UTF8.GetString(characters).Select(c => c.ToString()).ToList();
+			_offset = offset;
+		}
+
+		/// <summary>
+		/// 数制格式化器
+		/// </summary>
+		/// <param name="characters">符号集</param>
+		/// <param name="offset">起始值偏移</param>
+		public UnicodeFormater(ReadOnlySpan<char> characters, byte offset = 0)
+		{
+			if (characters == null || characters.Length == 0)
+			{
+				throw new ArgumentException("符号集不能为空");
+			}
+
+			Characters = characters.ToString().Select(c => c.ToString()).ToList();
+			_offset = offset;
+		}
 
 #endif
 
-        /// <summary>
-        /// 数制格式化器
-        /// </summary>
-        /// <param name="characters">符号集</param>
-        /// <param name="offset">起始值偏移</param>
-        public UnicodeFormater(byte[] characters, byte offset = 0)
-        {
-            if (characters == null || characters.Length == 0)
-            {
-                throw new ArgumentException("符号集不能为空");
-            }
-
-            Characters = Encoding.UTF8.GetString(characters).Select(c => c.ToString()).ToList(); ;
-            _offset = offset;
-        }
-
-        /// <summary>
-        /// 数制格式化器
-        /// </summary>
-        /// <param name="characters">符号集</param>
-        /// <param name="offset">起始值偏移</param>
-        public UnicodeFormater(char[] characters, byte offset = 0)
-        {
-            if (characters == null || characters.Length == 0)
-            {
-                throw new ArgumentException("符号集不能为空");
-            }
-
-            Characters = characters.Select(c => c.ToString()).ToList(); ;
-            _offset = offset;
-        }
-
-        /// <summary>
-        /// 数制格式化器
-        /// </summary>
-        /// <param name="base">进制</param>
-        /// <param name="offset">起始值偏移</param>
-        public UnicodeFormater(byte @base, byte offset = 0)
-        {
-            Characters = @base switch
-            {
-                <= 2 => new List<string>() { "0", "1" },
-                > 2 and < 65 => "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/".Substring(0, @base).Select(c => c.ToString()).ToList(),
-                >= 65 and <= 91 => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,-.:;<=>?@[]^_`{|}~\"".Substring(0, @base).Select(c => c.ToString()).ToList(),
-                _ => throw new ArgumentException("默认进制最大支持91进制")
-            };
-
-            if (offset >= @base)
-            {
-                throw new ArgumentException("偏移量不能超过进制基数" + @base);
-            }
-
-            _offset = offset;
-        }
-
-        /// <summary>
-        /// 数字转换为指定的进制形式字符串
-        /// </summary>
-        /// <param name="number"></param>
-        /// <returns></returns>
-        public string ToString(long number)
-        {
-            int start = 0;
-            int resultOffset = 0;
-            if (_offset > 0)
-            {
-                start = 1;
-                resultOffset = _offset - 1;
-            }
-
-            number -= resultOffset;
-            List<string> result = new List<string>();
-            long t = Math.Abs(number);
-            while (t != 0)
-            {
-                var mod = t % Length;
-                t = Math.Abs(t / Length);
-                var character = Characters[Convert.ToInt32(mod) - start].ToString();
-                result.Insert(0, character);
-            }
-
-            if (number < 0)
-            {
-                result.Insert(0, "-");
-            }
-
-            return string.Join("", result);
-        }
-
-        /// <summary>
-        /// 数字转换为指定的进制形式字符串
-        /// </summary>
-        /// <param name="number"></param>
-        /// <returns></returns>
-        public string ToString(BigInteger number)
-        {
-            int start = 0;
-            int resultOffset = 0;
-            if (_offset > 0)
-            {
-                start = 1;
-                resultOffset = _offset - 1;
-            }
-
-            number = number - resultOffset;
-            List<string> result = new List<string>();
-            if (number < 0)
-            {
-                number = -number;
-                result.Add("0");
-            }
-
-            BigInteger t = number;
-
-            while (t != 0)
-            {
-                var mod = t % Length;
-                t = BigInteger.Abs(BigInteger.Divide(t, Length));
-                var character = Characters[(int)mod - start].ToString();
-                result.Insert(0, character);
-            }
-
-            return string.Join("", result);
-        }
-
-        /// <summary>
-        /// 指定字符串转换为指定进制的数字形式
-        /// </summary>
-        /// <param name="str"></param>
-        /// <returns></returns>
-        public long FromString(string str)
-        {
-            byte start = 0;
-            int resultOffset = 0;
-            if (_offset > 0)
-            {
-                start = 1;
-                resultOffset = _offset - 1;
-            }
-
-            int j = 0;
-            if (_emojiMode)
-            {
-                var emoji = new List<string>();
-                foreach (Match m in EmojiRegex.Matches(str))
-                {
-                    emoji.Add(m.Value);
-                }
-
-                emoji.Reverse();
-                return emoji.Where(Characters.Contains).Sum(ch => (Characters.IndexOf(ch) + start) * (long)Math.Pow(Length, j++)) + resultOffset;
-            }
-
-            var chars = str.ToCharArray();
-            Array.Reverse(chars);
-            return chars.Where(c => Characters.Contains(c.ToString())).Sum(ch => (Characters.IndexOf(ch.ToString()) + start) * (long)Math.Pow(Length, j++)) + resultOffset;
-        }
-
-        /// <summary>
-        /// 指定字符串转换为指定进制的大数形式
-        /// </summary>
-        /// <param name="str"></param>
-        /// <returns></returns>
-        public BigInteger FromStringBig(string str)
-        {
-            byte start = 0;
-            int resultOffset = 0;
-            if (_offset > 0)
-            {
-                start = 1;
-                resultOffset = _offset - 1;
-            }
-            int j = 0;
-            if (_emojiMode)
-            {
-                var emoji = new List<string>();
-                foreach (Match m in EmojiRegex.Matches(str))
-                {
-                    emoji.Add(m.Value);
-                }
-                emoji.Reverse();
-                return emoji.Where(Characters.Contains).Aggregate(BigInteger.Zero, (current, c) => current + (Characters.IndexOf(c) + start) * BigInteger.Pow(Length, j++)) + resultOffset;
-            }
-
-            var charArray = str.ToCharArray();
-            Array.Reverse(charArray);
-            var chars = charArray.Where(c => Characters.Contains(c.ToString()));
-            return chars.Aggregate(BigInteger.Zero, (current, c) => current + (Characters.IndexOf(c.ToString()) + start) * BigInteger.Pow(Length, j++)) + resultOffset;
-        }
-
-        /// <summary>Returns a string that represents the current object.</summary>
-        /// <returns>A string that represents the current object.</returns>
-        public override string ToString()
-        {
-            return Length + "进制模式,进制符:" + Characters.Join("");
-        }
-    }
+		/// <summary>
+		/// 数制格式化器
+		/// </summary>
+		/// <param name="characters">符号集</param>
+		/// <param name="offset">起始值偏移</param>
+		public UnicodeFormater(byte[] characters, byte offset = 0)
+		{
+			if (characters == null || characters.Length == 0)
+			{
+				throw new ArgumentException("符号集不能为空");
+			}
+
+			Characters = Encoding.UTF8.GetString(characters).Select(c => c.ToString()).ToList();
+			_offset = offset;
+		}
+
+		/// <summary>
+		/// 数制格式化器
+		/// </summary>
+		/// <param name="characters">符号集</param>
+		/// <param name="offset">起始值偏移</param>
+		public UnicodeFormater(char[] characters, byte offset = 0)
+		{
+			if (characters == null || characters.Length == 0)
+			{
+				throw new ArgumentException("符号集不能为空");
+			}
+
+			Characters = characters.Select(c => c.ToString()).ToList();
+			_offset = offset;
+		}
+
+		/// <summary>
+		/// 数制格式化器
+		/// </summary>
+		/// <param name="base">进制</param>
+		/// <param name="offset">起始值偏移</param>
+		public UnicodeFormater(byte @base, byte offset = 0)
+		{
+			Characters = @base switch
+			{
+				<= 2 => new List<string>() { "0", "1" },
+				> 2 and < 65 => "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/".Substring(0, @base).Select(c => c.ToString()).ToList(),
+				>= 65 and <= 91 => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,-.:;<=>?@[]^_`{|}~\"".Substring(0, @base).Select(c => c.ToString()).ToList(),
+				_ => throw new ArgumentException("默认进制最大支持91进制")
+			};
+
+			if (offset >= @base)
+			{
+				throw new ArgumentException("偏移量不能超过进制基数" + @base);
+			}
+
+			_offset = offset;
+		}
+
+		/// <summary>
+		/// 数字转换为指定的进制形式字符串
+		/// </summary>
+		/// <param name="number"></param>
+		/// <returns></returns>
+		public string ToString(long number)
+		{
+			int start = 0;
+			int resultOffset = 0;
+			if (_offset > 0)
+			{
+				start = 1;
+				resultOffset = _offset - 1;
+			}
+
+			number -= resultOffset;
+			List<string> result = new List<string>();
+			long t = Math.Abs(number);
+			while (t != 0)
+			{
+				var mod = t % Length;
+				t = Math.Abs(t / Length);
+				var character = Characters[Convert.ToInt32(mod) - start];
+				result.Insert(0, character);
+			}
+
+			if (number < 0)
+			{
+				result.Insert(0, "-");
+			}
+
+			return string.Join("", result);
+		}
+
+		/// <summary>
+		/// 数字转换为指定的进制形式字符串
+		/// </summary>
+		/// <param name="number"></param>
+		/// <returns></returns>
+		public string ToString(BigInteger number)
+		{
+			int start = 0;
+			int resultOffset = 0;
+			if (_offset > 0)
+			{
+				start = 1;
+				resultOffset = _offset - 1;
+			}
+
+			number = number - resultOffset;
+			List<string> result = new List<string>();
+			if (number < 0)
+			{
+				number = -number;
+				result.Add("0");
+			}
+
+			BigInteger t = number;
+
+			while (t != 0)
+			{
+				var mod = t % Length;
+				t = BigInteger.Abs(BigInteger.Divide(t, Length));
+				var character = Characters[(int)mod - start];
+				result.Insert(0, character);
+			}
+
+			return string.Join("", result);
+		}
+
+		/// <summary>
+		/// 指定字符串转换为指定进制的数字形式
+		/// </summary>
+		/// <param name="str"></param>
+		/// <returns></returns>
+		public long FromString(string str)
+		{
+			byte start = 0;
+			int resultOffset = 0;
+			if (_offset > 0)
+			{
+				start = 1;
+				resultOffset = _offset - 1;
+			}
+
+			int j = 0;
+			var emoji = new List<string>();
+			var enumerator = StringInfo.GetTextElementEnumerator(str);
+			while (enumerator.MoveNext())
+			{
+				emoji.Add(enumerator.GetTextElement());
+			}
+
+			emoji.Reverse();
+			return emoji.Where(Characters.Contains).Sum(ch => (Characters.IndexOf(ch) + start) * (long)Math.Pow(Length, j++)) + resultOffset;
+		}
+
+		/// <summary>
+		/// 指定字符串转换为指定进制的大数形式
+		/// </summary>
+		/// <param name="str"></param>
+		/// <returns></returns>
+		public BigInteger FromStringBig(string str)
+		{
+			byte start = 0;
+			int resultOffset = 0;
+			if (_offset > 0)
+			{
+				start = 1;
+				resultOffset = _offset - 1;
+			}
+			int j = 0;
+			var emoji = new List<string>();
+			var enumerator = StringInfo.GetTextElementEnumerator(str);
+			while (enumerator.MoveNext())
+			{
+				emoji.Add(enumerator.GetTextElement());
+			}
+			emoji.Reverse();
+			return emoji.Where(Characters.Contains).Aggregate(BigInteger.Zero, (current, c) => current + (Characters.IndexOf(c) + start) * BigInteger.Pow(Length, j++)) + resultOffset;
+		}
+
+		/// <summary>Returns a string that represents the current object.</summary>
+		/// <returns>A string that represents the current object.</returns>
+		public override string ToString()
+		{
+			return Length + "进制模式,进制符:" + Characters.Join("");
+		}
+	}
 }

+ 10 - 3
Masuit.Tools.Abstractions/Systems/SnowFlake.cs

@@ -56,9 +56,16 @@ namespace Masuit.Tools.Systems
 		/// 默认构造函数
 		/// </summary>
 		public SnowFlake()
+		{
+		}
+
+		/// <summary>
+		/// 默认构造函数
+		/// </summary>
+		static SnowFlake()
 		{
 			var bytes = NetworkInterface.GetAllNetworkInterfaces().FirstOrDefault().GetPhysicalAddress().GetAddressBytes();
-			Snowflakes(bytes[4] << 2 | bytes[5]);
+			SetMachienId(bytes[4] << 2 | bytes[5]);
 		}
 
 		/// <summary>
@@ -67,10 +74,10 @@ namespace Masuit.Tools.Systems
 		/// <param name="machineId">机器码</param>
 		public SnowFlake(long machineId)
 		{
-			Snowflakes(machineId);
+			SetMachienId(machineId);
 		}
 
-		private void Snowflakes(long machineId)
+		public static void SetMachienId(long machineId)
 		{
 			if (machineId >= 0)
 			{

+ 12 - 1
Masuit.Tools.Abstractions/Systems/SnowFlakeNew.cs

@@ -11,7 +11,7 @@ namespace Masuit.Tools.Systems;
 /// </summary>
 public class SnowFlakeNew
 {
-	private readonly long _workerId; //机器ID
+	private static long _workerId; //机器ID
 	private const long Twepoch = 1692079923000L; //唯一时间随机量
 	private static long Offset = 7783685984256L; //起始偏移量
 	private static long _sequence;
@@ -45,6 +45,12 @@ public class SnowFlakeNew
 	/// 默认构造函数
 	/// </summary>
 	public SnowFlakeNew()
+	{ }
+
+	/// <summary>
+	/// 默认构造函数
+	/// </summary>
+	static SnowFlakeNew()
 	{
 		var bytes = NetworkInterface.GetAllNetworkInterfaces().FirstOrDefault().GetPhysicalAddress().GetAddressBytes();
 		_workerId = bytes[4] << 2 | bytes[5];
@@ -55,6 +61,11 @@ public class SnowFlakeNew
 	/// </summary>
 	/// <param name="machineId">机器码</param>
 	public SnowFlakeNew(int machineId)
+	{
+		SetMachienId(machineId);
+	}
+
+	public static void SetMachienId(long machineId)
 	{
 		if (machineId >= 0)
 		{

+ 1 - 1
Masuit.Tools.AspNetCore/Masuit.Tools.AspNetCore.csproj

@@ -18,7 +18,7 @@
         <Product>Masuit.Tools.AspNetCore</Product>
         <PackageId>Masuit.Tools.AspNetCore</PackageId>
         <LangVersion>latest</LangVersion>
-        <Version>1.2.6.7</Version>
+        <Version>1.2.6.8</Version>
         <RepositoryType></RepositoryType>
         <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
         <FileVersion>1.1.9</FileVersion>

+ 1 - 1
Masuit.Tools.Core/Masuit.Tools.Core.csproj

@@ -6,7 +6,7 @@
 官网教程:https://tools.masuit.org
 github:https://github.com/ldqk/Masuit.Tools
         </Description>
-        <Version>2.6.6.5</Version>
+        <Version>2.6.6.6</Version>
         <Copyright>Copyright © 懒得勤快</Copyright>
         <PackageProjectUrl>https://github.com/ldqk/Masuit.Tools</PackageProjectUrl>
         <PackageTags>Masuit.Tools,工具库,Utility,Crypt,Extensions</PackageTags>

+ 1 - 1
Masuit.Tools.Net45/package.nuspec

@@ -2,7 +2,7 @@
 <package>
   <metadata>
     <id>Masuit.Tools.Net45</id>
-    <version>2.6.6.3</version>
+    <version>2.6.6.6</version>
     <title>Masuit.Tools</title>
     <authors>懒得勤快</authors>
     <owners>masuit.com</owners>

+ 1 - 1
Masuit.Tools/package.nuspec

@@ -2,7 +2,7 @@
 <package>
   <metadata>
     <id>Masuit.Tools.Net</id>
-    <version>2.6.6.3</version>
+    <version>2.6.6.6</version>
     <title>Masuit.Tools</title>
     <authors>懒得勤快</authors>
     <owners>masuit.com</owners>

+ 18 - 4
README.md

@@ -216,7 +216,7 @@ var num=123.45.ToChineseNumber(); // 一百二十三点四五
 可用于生成短id,短hash,随机字符串等操作,纯数学运算。
 
 ```csharp
-NumberFormater nf = new NumberFormater(36);//内置2-91进制的转换
+NumberFormater nf = new NumberFormater(36);//内置2-95进制的转换
 //NumberFormater nf = new NumberFormater("0123456789abcdefghijklmnopqrstuvwxyz");// 自定义进制字符,可用于生成验证码,自定义字符可支持任意进制,你传1w个字符进去那就支持一万进制(手动狗头)
 string s36 = nf.ToString(12345678);
 long num = nf.FromString("7clzi");
@@ -276,11 +276,25 @@ Console.WriteLine("执行for循环100000次耗时"+time+"s");
 ### 7.产生分布式唯一有序短id(雪花id)
 
 ```csharp
+// 实例调用
 var sf = SnowFlake.GetInstance();
-string token = sf.GetUniqueId();// rcofqodori0w
-string token = SnowFlake.NewId;// rcofqodori0w
+string id = sf.GetUniqueId();// rcofqodori0w
+var sfn = SnowFlakeNew.GetInstance(); // 改良版雪花id,对时间回拨不敏感
+string id = sfn.GetUniqueId();// vmbq8q3s3zul
+
+// 静态调用
+string id = SnowFlake.NewId;// rcofqodori0w
 string shortId = sf.GetUniqueShortId(8);// qodw9728
-string token = SnowFlakeNew.NewId;// 改良版雪花id,对时间回拨不敏感
+string id = SnowFlakeNew.NewId;// 改良版雪花id,对时间回拨不敏感
+
+// 全局设置
+SnowFlake.SetMachienId(1); // 设置机器id
+SnowFlake.SetInitialOffset(4219864516915105792); // 设置起始偏移量
+SnowFlake.SetNumberFormater(new NumberFormater("0123456789abcdefghijklmnopqrstuvwxyz._-!")); // 设置数制格式化器
+
+SnowFlakeNew.SetMachienId(1); // 设置机器id
+SnowFlakeNew.SetInitialOffset(4219864516915105792); // 设置起始偏移量
+SnowFlakeNew.SetNumberFormater(new NumberFormater("0123456789abcdefghijklmnopqrstuvwxyz._-!")); // 设置数制格式化器
 ```
 
 ```csharp