StringExtensions.cs 17 KB

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