StringExtensions.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. using DnsClient;
  2. using Masuit.Tools.Strings;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Diagnostics;
  6. using System.Linq;
  7. using System.Net;
  8. using System.Net.Sockets;
  9. using System.Numerics;
  10. using System.Text;
  11. using System.Text.RegularExpressions;
  12. namespace Masuit.Tools
  13. {
  14. public static partial class StringExtensions
  15. {
  16. /// <summary>
  17. /// 字符串转时间
  18. /// </summary>
  19. /// <param name="value"></param>
  20. /// <returns></returns>
  21. public static DateTime ToDateTime(this string value)
  22. {
  23. DateTime.TryParse(value, out var result);
  24. return result;
  25. }
  26. /// <summary>
  27. /// 字符串转Guid
  28. /// </summary>
  29. /// <param name="s"></param>
  30. /// <returns></returns>
  31. public static Guid ToGuid(this string s)
  32. {
  33. return Guid.Parse(s);
  34. }
  35. /// <summary>
  36. /// 根据正则替换
  37. /// </summary>
  38. /// <param name="input"></param>
  39. /// <param name="regex">正则表达式</param>
  40. /// <param name="replacement">新内容</param>
  41. /// <returns></returns>
  42. public static string Replace(this string input, Regex regex, string replacement)
  43. {
  44. return regex.Replace(input, replacement);
  45. }
  46. /// <summary>
  47. /// 生成唯一短字符串
  48. /// </summary>
  49. /// <param name="str"></param>
  50. /// <param name="chars">可用字符数数量,0-9,a-z,A-Z</param>
  51. /// <returns></returns>
  52. public static string CreateShortToken(this string str, byte chars = 36)
  53. {
  54. var nf = new NumberFormater(chars);
  55. return nf.ToString((DateTime.Now.Ticks - 630822816000000000) * 100 + Stopwatch.GetTimestamp() % 100);
  56. }
  57. /// <summary>
  58. /// 任意进制转十进制
  59. /// </summary>
  60. /// <param name="str"></param>
  61. /// <param name="bin">进制</param>
  62. /// <returns></returns>
  63. public static long FromBinary(this string str, int bin)
  64. {
  65. var nf = new NumberFormater(bin);
  66. return nf.FromString(str);
  67. }
  68. /// <summary>
  69. /// 任意进制转大数十进制
  70. /// </summary>
  71. /// <param name="str"></param>
  72. /// <param name="bin">进制</param>
  73. /// <returns></returns>
  74. public static BigInteger FromBinaryBig(this string str, int bin)
  75. {
  76. var nf = new NumberFormater(bin);
  77. return nf.FromStringBig(str);
  78. }
  79. #region 检测字符串中是否包含列表中的关键词
  80. /// <summary>
  81. /// 检测字符串中是否包含列表中的关键词
  82. /// </summary>
  83. /// <param name="s">源字符串</param>
  84. /// <param name="keys">关键词列表</param>
  85. /// <param name="ignoreCase">忽略大小写</param>
  86. /// <returns></returns>
  87. public static bool Contains(this string s, IEnumerable<string> keys, bool ignoreCase = true)
  88. {
  89. if (!keys.Any() || string.IsNullOrEmpty(s))
  90. {
  91. return false;
  92. }
  93. if (ignoreCase)
  94. {
  95. return Regex.IsMatch(s, string.Join("|", keys), RegexOptions.IgnoreCase);
  96. }
  97. return Regex.IsMatch(s, string.Join("|", keys));
  98. }
  99. /// <summary>
  100. /// 判断是否包含符号
  101. /// </summary>
  102. /// <param name="str"></param>
  103. /// <param name="symbols"></param>
  104. /// <returns></returns>
  105. public static bool ContainsSymbol(this string str, params string[] symbols)
  106. {
  107. return str switch
  108. {
  109. null => false,
  110. string a when string.IsNullOrEmpty(a) => false,
  111. string a when a == string.Empty => false,
  112. _ => symbols.Any(t => str.Contains(t))
  113. };
  114. }
  115. #endregion 检测字符串中是否包含列表中的关键词
  116. /// <summary>
  117. /// 判断字符串是否为空或""
  118. /// </summary>
  119. /// <param name="s"></param>
  120. /// <returns></returns>
  121. public static bool IsNullOrEmpty(this string s)
  122. {
  123. return string.IsNullOrEmpty(s);
  124. }
  125. /// <summary>
  126. /// 字符串掩码
  127. /// </summary>
  128. /// <param name="s">字符串</param>
  129. /// <param name="mask">掩码符</param>
  130. /// <returns></returns>
  131. public static string Mask(this string s, char mask = '*')
  132. {
  133. if (string.IsNullOrWhiteSpace(s?.Trim()))
  134. {
  135. return s;
  136. }
  137. s = s.Trim();
  138. string masks = mask.ToString().PadLeft(4, mask);
  139. return s.Length switch
  140. {
  141. _ when s.Length >= 11 => Regex.Replace(s, @"(\w{3})\w*(\w{4})", $"$1{masks}$2"),
  142. _ when s.Length == 10 => Regex.Replace(s, @"(\w{3})\w*(\w{3})", $"$1{masks}$2"),
  143. _ when s.Length == 9 => Regex.Replace(s, @"(\w{2})\w*(\w{3})", $"$1{masks}$2"),
  144. _ when s.Length == 8 => Regex.Replace(s, @"(\w{2})\w*(\w{2})", $"$1{masks}$2"),
  145. _ when s.Length == 7 => Regex.Replace(s, @"(\w{1})\w*(\w{2})", $"$1{masks}$2"),
  146. _ when s.Length >= 2 && s.Length < 7 => Regex.Replace(s, @"(\w{1})\w*(\w{1})", $"$1{masks}$2"),
  147. _ => s + masks
  148. };
  149. }
  150. #region Email
  151. /// <summary>
  152. /// 匹配Email
  153. /// </summary>
  154. /// <param name="s">源字符串</param>
  155. /// <param name="valid">是否验证有效性</param>
  156. /// <returns>匹配对象;是否匹配成功,若返回true,则会得到一个Match对象,否则为null</returns>
  157. public static (bool isMatch, Match match) MatchEmail(this string s, bool valid = false)
  158. {
  159. if (string.IsNullOrEmpty(s) || s.Length < 7)
  160. {
  161. return (false, null);
  162. }
  163. Match match = Regex.Match(s, @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");
  164. var isMatch = match.Success;
  165. if (isMatch && valid)
  166. {
  167. var nslookup = new LookupClient();
  168. var query = nslookup.Query(s.Split('@')[1], QueryType.MX).Answers.MxRecords().SelectMany(r => Dns.GetHostAddresses(r.Exchange.Value)).ToList();
  169. isMatch = query.Any(ip => !ip.IsPrivateIP());
  170. }
  171. return (isMatch, match);
  172. }
  173. /// <summary>
  174. /// 邮箱掩码
  175. /// </summary>
  176. /// <param name="s">邮箱</param>
  177. /// <param name="mask">掩码</param>
  178. /// <returns></returns>
  179. public static string MaskEmail(this string s, char mask = '*')
  180. {
  181. return !MatchEmail(s).isMatch ? s : s.Replace(s.Substring(0, s.LastIndexOf("@")), Mask(s.Substring(0, s.LastIndexOf("@")), mask));
  182. }
  183. #endregion Email
  184. #region 匹配完整的URL
  185. /// <summary>
  186. /// 匹配完整格式的URL
  187. /// </summary>
  188. /// <param name="s">源字符串</param>
  189. /// <param name="isMatch">是否匹配成功,若返回true,则会得到一个Match对象,否则为null</param>
  190. /// <returns>匹配对象</returns>
  191. public static Uri MatchUrl(this string s, out bool isMatch)
  192. {
  193. try
  194. {
  195. var uri = new Uri(s);
  196. isMatch = Dns.GetHostAddresses(uri.Host).Any(ip => !ip.IsPrivateIP());
  197. return uri;
  198. }
  199. catch
  200. {
  201. isMatch = false;
  202. return null;
  203. }
  204. }
  205. /// <summary>
  206. /// 匹配完整格式的URL
  207. /// </summary>
  208. /// <param name="s">源字符串</param>
  209. /// <returns>是否匹配成功</returns>
  210. public static bool MatchUrl(this string s)
  211. {
  212. MatchUrl(s, out var isMatch);
  213. return isMatch;
  214. }
  215. #endregion 匹配完整的URL
  216. #region 权威校验身份证号码
  217. /// <summary>
  218. /// 根据GB11643-1999标准权威校验中国身份证号码的合法性
  219. /// </summary>
  220. /// <param name="s">源字符串</param>
  221. /// <returns>是否匹配成功</returns>
  222. public static bool MatchIdentifyCard(this string s)
  223. {
  224. string address = "11x22x35x44x53x12x23x36x45x54x13x31x37x46x61x14x32x41x50x62x15x33x42x51x63x21x34x43x52x64x65x71x81x82x91";
  225. if (s.Length == 18)
  226. {
  227. if (long.TryParse(s.Remove(17), out var n) == false || n < Math.Pow(10, 16) || long.TryParse(s.Replace('x', '0').Replace('X', '0'), out n) == false)
  228. {
  229. return false; //数字验证
  230. }
  231. if (address.IndexOf(s.Remove(2), StringComparison.Ordinal) == -1)
  232. {
  233. return false; //省份验证
  234. }
  235. string birth = s.Substring(6, 8).Insert(6, "-").Insert(4, "-");
  236. if (!DateTime.TryParse(birth, out _))
  237. {
  238. return false; //生日验证
  239. }
  240. string[] arrVarifyCode = ("1,0,x,9,8,7,6,5,4,3,2").Split(',');
  241. string[] wi = ("7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2").Split(',');
  242. char[] ai = s.Remove(17).ToCharArray();
  243. int sum = 0;
  244. for (int i = 0; i < 17; i++)
  245. {
  246. sum += wi[i].ToInt32() * ai[i].ToString().ToInt32();
  247. }
  248. Math.DivRem(sum, 11, out var y);
  249. return arrVarifyCode[y] == s.Substring(17, 1).ToLower();
  250. }
  251. if (s.Length == 15)
  252. {
  253. if (long.TryParse(s, out var n) == false || n < Math.Pow(10, 14))
  254. {
  255. return false; //数字验证
  256. }
  257. if (address.IndexOf(s.Remove(2), StringComparison.Ordinal) == -1)
  258. {
  259. return false; //省份验证
  260. }
  261. string birth = s.Substring(6, 6).Insert(4, "-").Insert(2, "-");
  262. return DateTime.TryParse(birth, out _);
  263. }
  264. return false;
  265. }
  266. #endregion 权威校验身份证号码
  267. #region IP地址
  268. /// <summary>
  269. /// 校验IP地址的正确性,同时支持IPv4和IPv6
  270. /// </summary>
  271. /// <param name="s">源字符串</param>
  272. /// <param name="isMatch">是否匹配成功,若返回true,则会得到一个Match对象,否则为null</param>
  273. /// <returns>匹配对象</returns>
  274. public static IPAddress MatchInetAddress(this string s, out bool isMatch)
  275. {
  276. isMatch = IPAddress.TryParse(s, out var ip);
  277. return ip;
  278. }
  279. /// <summary>
  280. /// 校验IP地址的正确性,同时支持IPv4和IPv6
  281. /// </summary>
  282. /// <param name="s">源字符串</param>
  283. /// <returns>是否匹配成功</returns>
  284. public static bool MatchInetAddress(this string s)
  285. {
  286. MatchInetAddress(s, out var success);
  287. return success;
  288. }
  289. /// <summary>
  290. /// IP地址转换成数字
  291. /// </summary>
  292. /// <param name="addr">IP地址</param>
  293. /// <returns>数字,输入无效IP地址返回0</returns>
  294. public static uint IPToID(this string addr)
  295. {
  296. if (!IPAddress.TryParse(addr, out var ip))
  297. {
  298. return 0;
  299. }
  300. byte[] bInt = ip.GetAddressBytes();
  301. if (BitConverter.IsLittleEndian)
  302. {
  303. Array.Reverse(bInt);
  304. }
  305. return BitConverter.ToUInt32(bInt, 0);
  306. }
  307. /// <summary>
  308. /// 判断IP是否是私有地址
  309. /// </summary>
  310. /// <param name="ip"></param>
  311. /// <returns></returns>
  312. public static bool IsPrivateIP(this string ip)
  313. {
  314. if (MatchInetAddress(ip))
  315. {
  316. return IPAddress.Parse(ip).IsPrivateIP();
  317. }
  318. throw new ArgumentException(ip + "不是一个合法的ip地址");
  319. }
  320. /// <summary>
  321. /// 判断IP地址在不在某个IP地址段
  322. /// </summary>
  323. /// <param name="input">需要判断的IP地址</param>
  324. /// <param name="begin">起始地址</param>
  325. /// <param name="ends">结束地址</param>
  326. /// <returns></returns>
  327. public static bool IpAddressInRange(this string input, string begin, string ends)
  328. {
  329. uint current = input.IPToID();
  330. return current >= begin.IPToID() && current <= ends.IPToID();
  331. }
  332. #endregion IP地址
  333. #region 校验手机号码的正确性
  334. /// <summary>
  335. /// 匹配手机号码
  336. /// </summary>
  337. /// <param name="s">源字符串</param>
  338. /// <param name="isMatch">是否匹配成功,若返回true,则会得到一个Match对象,否则为null</param>
  339. /// <returns>匹配对象</returns>
  340. public static Match MatchPhoneNumber(this string s, out bool isMatch)
  341. {
  342. if (string.IsNullOrEmpty(s))
  343. {
  344. isMatch = false;
  345. return null;
  346. }
  347. Match match = Regex.Match(s, @"^((1[3,5,6,8][0-9])|(14[5,7])|(17[0,1,3,6,7,8])|(19[8,9]))\d{8}$");
  348. isMatch = match.Success;
  349. return isMatch ? match : null;
  350. }
  351. /// <summary>
  352. /// 匹配手机号码
  353. /// </summary>
  354. /// <param name="s">源字符串</param>
  355. /// <returns>是否匹配成功</returns>
  356. public static bool MatchPhoneNumber(this string s)
  357. {
  358. MatchPhoneNumber(s, out bool success);
  359. return success;
  360. }
  361. #endregion 校验手机号码的正确性
  362. #region Url
  363. /// <summary>
  364. /// 判断url是否是外部地址
  365. /// </summary>
  366. /// <param name="url"></param>
  367. /// <returns></returns>
  368. public static bool IsExternalAddress(this string url)
  369. {
  370. var uri = new Uri(url);
  371. switch (uri.HostNameType)
  372. {
  373. case UriHostNameType.Dns:
  374. var ipHostEntry = Dns.GetHostEntry(uri.DnsSafeHost);
  375. if (ipHostEntry.AddressList.Where(ipAddress => ipAddress.AddressFamily == AddressFamily.InterNetwork).Any(ipAddress => !ipAddress.IsPrivateIP()))
  376. {
  377. return true;
  378. }
  379. break;
  380. case UriHostNameType.IPv4:
  381. return !IPAddress.Parse(uri.DnsSafeHost).IsPrivateIP();
  382. }
  383. return false;
  384. }
  385. #endregion Url
  386. /// <summary>
  387. /// 转换成字节数组
  388. /// </summary>
  389. /// <param name="this"></param>
  390. /// <returns></returns>
  391. public static byte[] ToByteArray(this string @this)
  392. {
  393. return Activator.CreateInstance<ASCIIEncoding>().GetBytes(@this);
  394. }
  395. #region Crc32
  396. /// <summary>
  397. /// 获取字符串crc32签名
  398. /// </summary>
  399. /// <param name="s"></param>
  400. /// <returns></returns>
  401. public static string Crc32(this string s)
  402. {
  403. return string.Join(string.Empty, new Security.Crc32().ComputeHash(Encoding.UTF8.GetBytes(s)).Select(b => b.ToString("x2")));
  404. }
  405. /// <summary>
  406. /// 获取字符串crc64签名
  407. /// </summary>
  408. /// <param name="s"></param>
  409. /// <returns></returns>
  410. public static string Crc64(this string s)
  411. {
  412. return string.Join(string.Empty, new Security.Crc64().ComputeHash(Encoding.UTF8.GetBytes(s)).Select(b => b.ToString("x2")));
  413. }
  414. #endregion Crc32
  415. }
  416. }