StringExtensions.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  1. using DnsClient;
  2. using Masuit.Tools.DateTimeExt;
  3. using Masuit.Tools.Strings;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.Globalization;
  8. using System.Linq;
  9. using System.Net;
  10. using System.Net.Sockets;
  11. using System.Numerics;
  12. using System.Text;
  13. using System.Text.RegularExpressions;
  14. using System.Threading;
  15. using System.Threading.Tasks;
  16. using Masuit.Tools.Security;
  17. namespace Masuit.Tools
  18. {
  19. public static partial class StringExtensions
  20. {
  21. public static string Join(this IEnumerable<string> strs, string separate = ", ", bool removeEmptyEntry = false) => string.Join(separate, removeEmptyEntry ? strs.Where(s => !string.IsNullOrEmpty(s)) : strs);
  22. public static string Join<T>(this IEnumerable<T> strs, string separate = ", ", bool removeEmptyEntry = false) => string.Join(separate, removeEmptyEntry ? strs.Where(t => t != null) : strs);
  23. /// <summary>
  24. /// 字符串转时间
  25. /// </summary>
  26. /// <param name="value"></param>
  27. /// <returns></returns>
  28. public static DateTime ToDateTime(this string value)
  29. {
  30. DateTime.TryParse(value, out var result);
  31. return result;
  32. }
  33. /// <summary>
  34. /// 字符串转Guid
  35. /// </summary>
  36. /// <param name="s"></param>
  37. /// <returns></returns>
  38. public static Guid ToGuid(this string s)
  39. {
  40. return Guid.Parse(s);
  41. }
  42. /// <summary>
  43. /// 根据正则替换
  44. /// </summary>
  45. /// <param name="input"></param>
  46. /// <param name="regex">正则表达式</param>
  47. /// <param name="replacement">新内容</param>
  48. /// <returns></returns>
  49. public static string Replace(this string input, Regex regex, string replacement)
  50. {
  51. return regex.Replace(input, replacement);
  52. }
  53. /// <summary>
  54. /// 生成唯一短字符串
  55. /// </summary>
  56. /// <param name="str"></param>
  57. /// <param name="chars">可用字符数数量,0-9,a-z,A-Z</param>
  58. /// <returns></returns>
  59. public static string CreateShortToken(this string str, byte chars = 36)
  60. {
  61. var nf = new NumberFormater(chars);
  62. return nf.ToString((DateTime.Now.Ticks - 630822816000000000) * 100 + Stopwatch.GetTimestamp() % 100);
  63. }
  64. /// <summary>
  65. /// 任意进制转十进制
  66. /// </summary>
  67. /// <param name="str"></param>
  68. /// <param name="base">进制</param>
  69. /// <returns></returns>
  70. public static long FromBase(this string str, byte @base)
  71. {
  72. var nf = new NumberFormater(@base);
  73. return nf.FromString(str);
  74. }
  75. /// <summary>
  76. /// 任意进制转大数十进制
  77. /// </summary>
  78. /// <param name="str"></param>
  79. /// <param name="base">进制</param>
  80. /// <returns></returns>
  81. public static BigInteger FromBaseBig(this string str, byte @base)
  82. {
  83. var nf = new NumberFormater(@base);
  84. return nf.FromStringBig(str);
  85. }
  86. #region 检测字符串中是否包含列表中的关键词
  87. /// <summary>
  88. /// 检测字符串中是否包含列表中的关键词(快速匹配)
  89. /// </summary>
  90. /// <param name="s">源字符串</param>
  91. /// <param name="keys">关键词列表</param>
  92. /// <param name="ignoreCase">忽略大小写</param>
  93. /// <returns></returns>
  94. public static bool Contains(this string s, IEnumerable<string> keys, bool ignoreCase = true)
  95. {
  96. if (keys is not ICollection<string> array)
  97. {
  98. array = keys.ToArray();
  99. }
  100. if (array.Count == 0 || string.IsNullOrEmpty(s))
  101. {
  102. return false;
  103. }
  104. return ignoreCase ? array.Any(item => s.IndexOf(item, StringComparison.InvariantCultureIgnoreCase) >= 0) : array.Any(s.Contains);
  105. }
  106. /// <summary>
  107. /// 检测字符串中是否包含列表中的关键词(安全匹配)
  108. /// </summary>
  109. /// <param name="s">源字符串</param>
  110. /// <param name="keys">关键词列表</param>
  111. /// <param name="ignoreCase">忽略大小写</param>
  112. /// <returns></returns>
  113. public static bool ContainsSafety(this string s, IEnumerable<string> keys, bool ignoreCase = true)
  114. {
  115. if (keys is not ICollection<string> array)
  116. {
  117. array = keys.ToArray();
  118. }
  119. if (array.Count == 0 || string.IsNullOrEmpty(s))
  120. {
  121. return false;
  122. }
  123. bool flag = false;
  124. if (ignoreCase)
  125. {
  126. foreach (var item in array)
  127. {
  128. if (s.Contains(item))
  129. {
  130. flag = true;
  131. }
  132. }
  133. }
  134. else
  135. {
  136. foreach (var item in array)
  137. {
  138. if (s.IndexOf(item, StringComparison.InvariantCultureIgnoreCase) >= 0)
  139. {
  140. flag = true;
  141. }
  142. }
  143. }
  144. return flag;
  145. }
  146. /// <summary>
  147. /// 检测字符串中是否以列表中的关键词结尾
  148. /// </summary>
  149. /// <param name="s">源字符串</param>
  150. /// <param name="keys">关键词列表</param>
  151. /// <param name="ignoreCase">忽略大小写</param>
  152. /// <returns></returns>
  153. public static bool EndsWith(this string s, IEnumerable<string> keys, bool ignoreCase = true)
  154. {
  155. if (keys is not ICollection<string> array)
  156. {
  157. array = keys.ToArray();
  158. }
  159. if (array.Count == 0 || string.IsNullOrEmpty(s))
  160. {
  161. return false;
  162. }
  163. var pattern = $"({array.Select(Regex.Escape).Join("|")})$";
  164. return ignoreCase ? Regex.IsMatch(s, pattern, RegexOptions.IgnoreCase) : Regex.IsMatch(s, pattern);
  165. }
  166. /// <summary>
  167. /// 检测字符串中是否以列表中的关键词开始
  168. /// </summary>
  169. /// <param name="s">源字符串</param>
  170. /// <param name="keys">关键词列表</param>
  171. /// <param name="ignoreCase">忽略大小写</param>
  172. /// <returns></returns>
  173. public static bool StartsWith(this string s, IEnumerable<string> keys, bool ignoreCase = true)
  174. {
  175. if (keys is not ICollection<string> array)
  176. {
  177. array = keys.ToArray();
  178. }
  179. if (array.Count == 0 || string.IsNullOrEmpty(s))
  180. {
  181. return false;
  182. }
  183. var pattern = $"^({array.Select(Regex.Escape).Join("|")})";
  184. return ignoreCase ? Regex.IsMatch(s, pattern, RegexOptions.IgnoreCase) : Regex.IsMatch(s, pattern);
  185. }
  186. /// <summary>
  187. /// 检测字符串中是否包含列表中的关键词
  188. /// </summary>
  189. /// <param name="s">源字符串</param>
  190. /// <param name="regex">关键词列表</param>
  191. /// <param name="ignoreCase">忽略大小写</param>
  192. /// <returns></returns>
  193. public static bool RegexMatch(this string s, string regex, bool ignoreCase = true)
  194. {
  195. if (string.IsNullOrEmpty(regex) || string.IsNullOrEmpty(s))
  196. {
  197. return false;
  198. }
  199. if (ignoreCase)
  200. {
  201. return Regex.IsMatch(s, regex, RegexOptions.IgnoreCase);
  202. }
  203. return Regex.IsMatch(s, regex);
  204. }
  205. /// <summary>
  206. /// 检测字符串中是否包含列表中的关键词
  207. /// </summary>
  208. /// <param name="s">源字符串</param>
  209. /// <param name="regex">关键词列表</param>
  210. /// <returns></returns>
  211. public static bool RegexMatch(this string s, Regex regex) => !string.IsNullOrEmpty(s) && regex.IsMatch(s);
  212. #endregion 检测字符串中是否包含列表中的关键词
  213. /// <summary>
  214. /// 判断字符串是否为空或""
  215. /// </summary>
  216. /// <param name="s"></param>
  217. /// <returns></returns>
  218. public static bool IsNullOrEmpty(this string s)
  219. {
  220. return string.IsNullOrWhiteSpace(s) || s.Equals("null", StringComparison.CurrentCultureIgnoreCase);
  221. }
  222. /// <summary>
  223. /// 判断字符串不为空或""
  224. /// </summary>
  225. /// <param name="s"></param>
  226. /// <returns></returns>
  227. public static bool NotNullOrEmpty(this string s)
  228. {
  229. return !string.IsNullOrWhiteSpace(s) && !s.Equals("null", StringComparison.CurrentCultureIgnoreCase);
  230. }
  231. /// <summary>
  232. /// 转成非null
  233. /// </summary>
  234. /// <param name="s"></param>
  235. /// <returns></returns>
  236. public static string AsNotNull(this string s)
  237. {
  238. return string.IsNullOrEmpty(s) ? "" : s;
  239. }
  240. /// <summary>
  241. /// 转成非null
  242. /// </summary>
  243. /// <param name="s"></param>
  244. /// <param name="value">为空时的替换值</param>
  245. /// <returns></returns>
  246. public static string IfNullOrEmpty(this string s, string value)
  247. {
  248. return string.IsNullOrEmpty(s) ? value : s;
  249. }
  250. /// <summary>
  251. /// 转成非null
  252. /// </summary>
  253. /// <param name="s"></param>
  254. /// <param name="valueFactory">为空时的替换值函数</param>
  255. /// <returns></returns>
  256. public static string IfNullOrEmpty(this string s, Func<string> valueFactory)
  257. {
  258. return string.IsNullOrEmpty(s) ? valueFactory() : s;
  259. }
  260. /// <summary>
  261. /// 字符串掩码
  262. /// </summary>
  263. /// <param name="s">字符串</param>
  264. /// <param name="mask">掩码符</param>
  265. /// <returns></returns>
  266. public static string Mask(this string s, char mask = '*')
  267. {
  268. if (string.IsNullOrWhiteSpace(s?.Trim()))
  269. {
  270. return s;
  271. }
  272. s = s.Trim();
  273. string masks = mask.ToString().PadLeft(4, mask);
  274. return s.Length switch
  275. {
  276. >= 11 => Regex.Replace(s, "(.{3}).*(.{4})", $"$1{masks}$2"),
  277. 10 => Regex.Replace(s, "(.{3}).*(.{3})", $"$1{masks}$2"),
  278. 9 => Regex.Replace(s, "(.{2}).*(.{3})", $"$1{masks}$2"),
  279. 8 => Regex.Replace(s, "(.{2}).*(.{2})", $"$1{masks}$2"),
  280. 7 => Regex.Replace(s, "(.{1}).*(.{2})", $"$1{masks}$2"),
  281. 6 => Regex.Replace(s, "(.{1}).*(.{1})", $"$1{masks}$2"),
  282. _ => Regex.Replace(s, "(.{1}).*", $"$1{masks}")
  283. };
  284. }
  285. #region Email
  286. /// <summary>
  287. /// 匹配Email
  288. /// </summary>
  289. /// <param name="s">源字符串</param>
  290. /// <param name="valid">是否验证有效性</param>
  291. /// <returns>匹配对象;是否匹配成功,若返回true,则会得到一个Match对象,否则为null</returns>
  292. public static (bool isMatch, Match match) MatchEmail(this string s, bool valid = false)
  293. {
  294. if (string.IsNullOrEmpty(s) || s.Length < 7)
  295. {
  296. return (false, null);
  297. }
  298. var match = Regex.Match(s, @"[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n]+");
  299. var isMatch = match.Success;
  300. if (isMatch && valid)
  301. {
  302. var nslookup = new LookupClient();
  303. var task = nslookup.Query(s.Split('@')[1], QueryType.MX).Answers.MxRecords().SelectAsync(r => Dns.GetHostAddressesAsync(r.Exchange.Value).ContinueWith(t =>
  304. {
  305. if (t.IsCanceled || t.IsFaulted)
  306. {
  307. return new[]
  308. {
  309. IPAddress.Loopback
  310. };
  311. }
  312. return t.Result;
  313. }));
  314. isMatch = task.Result.SelectMany(a => a).Any(ip => !ip.IsPrivateIP());
  315. }
  316. return (isMatch, match);
  317. }
  318. /// <summary>
  319. /// 匹配Email
  320. /// </summary>
  321. /// <param name="s">源字符串</param>
  322. /// <param name="valid">是否验证有效性</param>
  323. /// <returns>匹配对象;是否匹配成功,若返回true,则会得到一个Match对象,否则为null</returns>
  324. public static async Task<(bool isMatch, Match match)> MatchEmailAsync(this string s, bool valid = false)
  325. {
  326. if (string.IsNullOrEmpty(s) || s.Length < 7)
  327. {
  328. return (false, null);
  329. }
  330. var match = Regex.Match(s, @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");
  331. var isMatch = match.Success;
  332. if (isMatch && valid)
  333. {
  334. var nslookup = new LookupClient();
  335. using var cts = new CancellationTokenSource(100);
  336. var query = await nslookup.QueryAsync(s.Split('@')[1], QueryType.MX, cancellationToken: cts.Token);
  337. var result = await query.Answers.MxRecords().SelectAsync(r => Dns.GetHostAddressesAsync(r.Exchange.Value).ContinueWith(t =>
  338. {
  339. if (t.IsCanceled || t.IsFaulted)
  340. {
  341. return new[] { IPAddress.Loopback };
  342. }
  343. return t.Result;
  344. }));
  345. isMatch = result.SelectMany(a => a).Any(ip => !ip.IsPrivateIP());
  346. }
  347. return (isMatch, match);
  348. }
  349. /// <summary>
  350. /// 邮箱掩码
  351. /// </summary>
  352. /// <param name="s">邮箱</param>
  353. /// <param name="mask">掩码</param>
  354. /// <returns></returns>
  355. public static string MaskEmail(this string s, char mask = '*')
  356. {
  357. var index = s.LastIndexOf("@");
  358. var oldValue = s.Substring(0, index);
  359. return !MatchEmail(s).isMatch ? s : s.Replace(oldValue, Mask(oldValue, mask));
  360. }
  361. #endregion Email
  362. #region 匹配完整的URL
  363. /// <summary>
  364. /// 匹配完整格式的URL
  365. /// </summary>
  366. /// <param name="s">源字符串</param>
  367. /// <param name="isMatch">是否匹配成功,若返回true,则会得到一个Match对象,否则为null</param>
  368. /// <returns>匹配对象</returns>
  369. public static Uri MatchUrl(this string s, out bool isMatch)
  370. {
  371. try
  372. {
  373. var uri = new Uri(s);
  374. isMatch = Dns.GetHostAddresses(uri.Host).Any(ip => !ip.IsPrivateIP());
  375. return uri;
  376. }
  377. catch
  378. {
  379. isMatch = false;
  380. return null;
  381. }
  382. }
  383. /// <summary>
  384. /// 匹配完整格式的URL
  385. /// </summary>
  386. /// <param name="s">源字符串</param>
  387. /// <returns>是否匹配成功</returns>
  388. public static bool MatchUrl(this string s)
  389. {
  390. MatchUrl(s, out var isMatch);
  391. return isMatch;
  392. }
  393. #endregion 匹配完整的URL
  394. #region 权威校验身份证号码
  395. /// <summary>
  396. /// 根据GB11643-1999标准权威校验中国身份证号码的合法性
  397. /// </summary>
  398. /// <param name="s">源字符串</param>
  399. /// <returns>是否匹配成功</returns>
  400. public static bool MatchIdentifyCard(this string s)
  401. {
  402. return s.Length switch
  403. {
  404. 18 => CheckChinaID18(s),
  405. 15 => CheckChinaID15(s),
  406. _ => false
  407. };
  408. }
  409. private static readonly string[] ChinaIDProvinceCodes = {
  410. "11", "12", "13", "14", "15",
  411. "21", "22", "23",
  412. "31", "32", "33", "34", "35", "36", "37",
  413. "41", "42", "43", "44", "45", "46",
  414. "50", "51", "52", "53", "54",
  415. "61", "62", "63", "64", "65",
  416. "71",
  417. "81",
  418. "82",
  419. "91"
  420. };
  421. private static bool CheckChinaID18(string ID)
  422. {
  423. ID = ID.ToUpper();
  424. Match m = Regex.Match(ID, @"\d{17}[\dX]", RegexOptions.IgnoreCase);
  425. if (!m.Success)
  426. {
  427. return false;
  428. }
  429. if (!ChinaIDProvinceCodes.Contains(ID.Substring(0, 2)))
  430. {
  431. return false;
  432. }
  433. CultureInfo zhCN = new CultureInfo("zh-CN", true);
  434. if (!DateTime.TryParseExact(ID.Substring(6, 8), "yyyyMMdd", zhCN, DateTimeStyles.None, out DateTime birthday))
  435. {
  436. return false;
  437. }
  438. if (!birthday.In(new DateTime(1800, 1, 1), DateTime.Today))
  439. {
  440. return false;
  441. }
  442. int[] factors = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
  443. int sum = 0;
  444. for (int i = 0; i < 17; i++)
  445. {
  446. sum += (ID[i] - '0') * factors[i];
  447. }
  448. int n = (12 - sum % 11) % 11;
  449. return n < 10 ? ID[17] - '0' == n : ID[17].Equals('X');
  450. }
  451. private static bool CheckChinaID15(string ID)
  452. {
  453. Match m = Regex.Match(ID, @"\d{15}", RegexOptions.IgnoreCase);
  454. if (!m.Success)
  455. {
  456. return false;
  457. }
  458. if (!ChinaIDProvinceCodes.Contains(ID.Substring(0, 2)))
  459. {
  460. return false;
  461. }
  462. CultureInfo zhCN = new CultureInfo("zh-CN", true);
  463. if (!DateTime.TryParseExact("19" + ID.Substring(6, 6), "yyyyMMdd", zhCN, DateTimeStyles.None, out DateTime birthday))
  464. {
  465. return false;
  466. }
  467. return birthday.In(new DateTime(1800, 1, 1), new DateTime(2000, 1, 1));
  468. }
  469. #endregion 权威校验身份证号码
  470. #region IP地址
  471. /// <summary>
  472. /// 校验IP地址的正确性,同时支持IPv4和IPv6
  473. /// </summary>
  474. /// <param name="s">源字符串</param>
  475. /// <param name="isMatch">是否匹配成功,若返回true,则会得到一个Match对象,否则为null</param>
  476. /// <returns>匹配对象</returns>
  477. public static IPAddress MatchInetAddress(this string s, out bool isMatch)
  478. {
  479. isMatch = IPAddress.TryParse(s, out var ip);
  480. return ip;
  481. }
  482. /// <summary>
  483. /// 校验IP地址的正确性,同时支持IPv4和IPv6
  484. /// </summary>
  485. /// <param name="s">源字符串</param>
  486. /// <returns>是否匹配成功</returns>
  487. public static bool MatchInetAddress(this string s)
  488. {
  489. MatchInetAddress(s, out var success);
  490. return success;
  491. }
  492. /// <summary>
  493. /// IP地址转换成数字
  494. /// </summary>
  495. /// <param name="addr">IP地址</param>
  496. /// <returns>数字,输入无效IP地址返回0</returns>
  497. public static uint IPToID(this string addr)
  498. {
  499. if (!IPAddress.TryParse(addr, out var ip))
  500. {
  501. return 0;
  502. }
  503. byte[] bInt = ip.GetAddressBytes();
  504. if (BitConverter.IsLittleEndian)
  505. {
  506. Array.Reverse(bInt);
  507. }
  508. return BitConverter.ToUInt32(bInt, 0);
  509. }
  510. /// <summary>
  511. /// IP地址转换成数字
  512. /// </summary>
  513. /// <param name="ip">IP地址</param>
  514. /// <returns>数字,输入无效IP地址返回0</returns>
  515. public static uint ToUInt32(this IPAddress ip)
  516. {
  517. byte[] bInt = ip.GetAddressBytes();
  518. if (BitConverter.IsLittleEndian)
  519. {
  520. Array.Reverse(bInt);
  521. }
  522. return BitConverter.ToUInt32(bInt, 0);
  523. }
  524. /// <summary>
  525. /// 判断IP是否是私有地址
  526. /// </summary>
  527. /// <param name="ip"></param>
  528. /// <returns></returns>
  529. public static bool IsPrivateIP(this string ip)
  530. {
  531. var address = MatchInetAddress(ip, out var b);
  532. return b && address.IsPrivateIP();
  533. }
  534. /// <summary>
  535. /// 判断IP地址在不在某个IP地址段
  536. /// </summary>
  537. /// <param name="input">需要判断的IP地址</param>
  538. /// <param name="begin">起始地址</param>
  539. /// <param name="ends">结束地址</param>
  540. /// <returns></returns>
  541. public static bool IpAddressInRange(this string input, string begin, string ends)
  542. {
  543. uint current = input.IPToID();
  544. return current >= begin.IPToID() && current <= ends.IPToID();
  545. }
  546. /// <summary>
  547. /// 判断IP地址在不在某个IP地址段
  548. /// </summary>
  549. /// <param name="input">需要判断的IP地址</param>
  550. /// <param name="begin">起始地址</param>
  551. /// <param name="ends">结束地址</param>
  552. /// <returns></returns>
  553. public static bool IpAddressInRange(this IPAddress input, IPAddress begin, IPAddress ends)
  554. {
  555. uint current = input.ToUInt32();
  556. return current >= begin.ToUInt32() && current <= ends.ToUInt32();
  557. }
  558. #endregion IP地址
  559. #region 校验手机号码的正确性
  560. /// <summary>
  561. /// 匹配手机号码
  562. /// </summary>
  563. /// <param name="s">源字符串</param>
  564. /// <returns>是否匹配成功</returns>
  565. public static bool MatchPhoneNumber(this string s)
  566. {
  567. return !string.IsNullOrEmpty(s) && s.Length == 11 && s[0] == '1' && (s[1] > '2' || s[1] <= '9') && long.TryParse(s, out _);
  568. }
  569. /// <summary>
  570. /// 匹配固话号码
  571. /// </summary>
  572. /// <param name="s">源字符串</param>
  573. /// <returns>是否匹配成功</returns>
  574. public static bool MatchLandline(this string s)
  575. {
  576. return Regex.IsMatch(s, @"^0\d{2,3}(?:-?\d{8}|-?\d{7})$");
  577. }
  578. /// <summary>
  579. /// 匹配企业的统一社会信用代码
  580. /// </summary>
  581. /// <param name="s">源字符串</param>
  582. /// <returns>是否匹配成功</returns>
  583. public static bool MatchUSCC(this string s)
  584. {
  585. return Regex.IsMatch(s, @"^([0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}|[1-9]\d{14})$");
  586. }
  587. #endregion 校验手机号码的正确性
  588. #region Url
  589. /// <summary>
  590. /// 判断url是否是外部地址
  591. /// </summary>
  592. /// <param name="url"></param>
  593. /// <returns></returns>
  594. public static bool IsExternalAddress(this string url)
  595. {
  596. var uri = new Uri(url);
  597. switch (uri.HostNameType)
  598. {
  599. case UriHostNameType.Dns:
  600. var ipHostEntry = Dns.GetHostEntry(uri.DnsSafeHost);
  601. if (ipHostEntry.AddressList.Where(ipAddress => ipAddress.AddressFamily == AddressFamily.InterNetwork).Any(ipAddress => !ipAddress.IsPrivateIP()))
  602. {
  603. return true;
  604. }
  605. break;
  606. case UriHostNameType.IPv4:
  607. return !IPAddress.Parse(uri.DnsSafeHost).IsPrivateIP();
  608. }
  609. return false;
  610. }
  611. #endregion Url
  612. /// <summary>
  613. /// 转换成字节数组
  614. /// </summary>
  615. /// <param name="this"></param>
  616. /// <returns></returns>
  617. public static byte[] ToByteArray(this string @this)
  618. {
  619. return Encoding.UTF8.GetBytes(@this);
  620. }
  621. #region Crc32
  622. /// <summary>
  623. /// 获取字符串crc32签名
  624. /// </summary>
  625. /// <param name="s"></param>
  626. /// <returns></returns>
  627. public static string Crc32(this string s)
  628. {
  629. using var crc32 = new Crc32();
  630. return string.Join(string.Empty, crc32.ComputeHash(Encoding.UTF8.GetBytes(s)).Select(b => b.ToString("x2")));
  631. }
  632. /// <summary>
  633. /// 获取字符串crc64签名
  634. /// </summary>
  635. /// <param name="s"></param>
  636. /// <returns></returns>
  637. public static string Crc64(this string s)
  638. {
  639. using var crc64 = new Crc64();
  640. return string.Join(string.Empty, crc64.ComputeHash(Encoding.UTF8.GetBytes(s)).Select(b => b.ToString("x2")));
  641. }
  642. #endregion Crc32
  643. #region 权威校验中国专利申请号/专利号
  644. /// <summary>
  645. /// 中国专利申请号(授权以后就是专利号)由两种组成
  646. /// 2003年9月30号以前的9位(不带校验位是8号),校验位之前可能还会有一个点,例如:00262311, 002623110 或 00262311.0
  647. /// 2003年10月1号以后的13位(不带校验位是12号),校验位之前可能还会有一个点,例如:200410018477, 2004100184779 或200410018477.9
  648. /// http://www.sipo.gov.cn/docs/pub/old/wxfw/zlwxxxggfw/hlwzljsxt/hlwzljsxtsyzn/201507/P020150713610193194682.pdf
  649. /// 上面的文档中均不包括校验算法,但是下面的校验算法没有问题
  650. /// </summary>
  651. /// <param name="patnum">源字符串</param>
  652. /// <returns>是否匹配成功</returns>
  653. public static bool MatchCNPatentNumber(this string patnum)
  654. {
  655. var patnumWithCheckbitPattern = new Regex(@"^
  656. (?<!\d)
  657. (?<patentnum>
  658. (?<basenum>
  659. (?<year>(?<old>8[5-9]|9[0-9]|0[0-3])|(?<new>[2-9]\d{3}))
  660. (?<sn>
  661. (?<patenttype>[12389])
  662. (?(old)\d{5}|(?(new)\d{7}))
  663. )
  664. )
  665. (?:
  666. \.?
  667. (?<checkbit>[0-9X])
  668. )?
  669. )
  670. (?!\d)
  671. $", RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase | RegexOptions.Multiline);
  672. var m = patnumWithCheckbitPattern.Match(patnum);
  673. if (!m.Success)
  674. {
  675. return false;
  676. }
  677. bool isPatnumTrue = true;
  678. patnum = patnum.ToUpper().Replace(".", "");
  679. if (patnum.Length == 9 || patnum.Length == 8)
  680. {
  681. byte[] factors8 = { 2, 3, 4, 5, 6, 7, 8, 9 };
  682. int year = Convert.ToUInt16(patnum.Substring(0, 2));
  683. year += year >= 85 ? (ushort)1900u : (ushort)2000u;
  684. if (year is >= 1985 or <= 2003)
  685. {
  686. int sum = 0;
  687. for (byte i = 0; i < 8; i++)
  688. {
  689. sum += factors8[i] * (patnum[i] - '0');
  690. }
  691. char checkbit = "0123456789X"[sum % 11];
  692. if (patnum.Length == 9 && checkbit != patnum[8])
  693. {
  694. isPatnumTrue = false;
  695. }
  696. }
  697. else
  698. {
  699. isPatnumTrue = false;
  700. }
  701. }
  702. else if (patnum.Length is 13 or 12)
  703. {
  704. byte[] factors12 = { 2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5 };
  705. int year = Convert.ToUInt16(patnum.Substring(0, 4));
  706. if (year >= 2003 && year <= DateTime.Now.Year)
  707. {
  708. int sum = 0;
  709. for (byte i = 0; i < 12; i++)
  710. {
  711. sum += factors12[i] * (patnum[i] - '0');
  712. }
  713. char checkbit = "0123456789X"[sum % 11];
  714. if (patnum.Length == 13)
  715. {
  716. if (checkbit != patnum[12])
  717. {
  718. isPatnumTrue = false;
  719. }
  720. }
  721. else
  722. {
  723. patnum += checkbit;
  724. }
  725. }
  726. else
  727. {
  728. isPatnumTrue = false;
  729. }
  730. }
  731. else
  732. {
  733. isPatnumTrue = false;
  734. }
  735. return isPatnumTrue;
  736. }
  737. #endregion 权威校验中国专利申请号/专利号
  738. /// <summary>
  739. /// 取字符串前{length}个字
  740. /// </summary>
  741. /// <param name="s"></param>
  742. /// <param name="length"></param>
  743. /// <returns></returns>
  744. public static string Take(this string s, int length)
  745. {
  746. return s.Length > length ? s.Substring(0, length) : s;
  747. }
  748. /// <summary>
  749. /// 对比字符串的汉明距离
  750. /// </summary>
  751. /// <param name="this"></param>
  752. /// <param name="that"></param>
  753. /// <returns></returns>
  754. public static int HammingDistance(this string @this, string that) => new SimHash(@this).HammingDistance(new SimHash(that));
  755. /// <summary>
  756. /// 匹配字符串是否包含emoji字符
  757. /// </summary>
  758. /// <param name="s"></param>
  759. /// <returns></returns>
  760. public static bool MatchEmoji(this string s)
  761. {
  762. return Regex.IsMatch(s, @"(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])");
  763. }
  764. /// <summary>
  765. /// 获取字符串的字符数
  766. /// </summary>
  767. /// <param name="str"></param>
  768. /// <returns></returns>
  769. public static int CharacterCount(this string str)
  770. {
  771. var enumerator = StringInfo.GetTextElementEnumerator(str);
  772. int length = 0;
  773. while (enumerator.MoveNext())
  774. {
  775. length++;
  776. }
  777. return length;
  778. }
  779. /// <summary>
  780. /// 获取字符串的字节数
  781. /// </summary>
  782. /// <param name="str"></param>
  783. /// <returns></returns>
  784. public static int BytesCount(this string str)
  785. {
  786. return Encoding.UTF8.GetByteCount(str);
  787. }
  788. /// <summary>
  789. /// 转半角(DBC case)
  790. /// </summary>
  791. /// <param name="input">任意字符串</param>
  792. /// <returns>半角字符串</returns>
  793. ///<remarks>
  794. ///全角空格为12288,半角空格为32(此处不必转空格)
  795. ///其他字符半角(33-126)与全角(65281-65374)的对应关系是:均相差65248
  796. ///</remarks>
  797. public static string ToDBC(this string input)
  798. {
  799. char[] c = input.ToCharArray();
  800. for (int i = 0; i < c.Length; i++)
  801. {
  802. if (c[i] == 12288)
  803. {
  804. c[i] = (char)32;
  805. continue;
  806. }
  807. if (c[i] > 65280 && c[i] < 65375)
  808. {
  809. c[i] = (char)(c[i] - 65248);
  810. }
  811. }
  812. return new string(c);
  813. }
  814. /// <summary>
  815. /// 转全角(SBC case)
  816. /// </summary>
  817. /// <param name="input">任意字符串</param>
  818. /// <returns>全角字符串</returns>
  819. ///<remarks>
  820. ///全角空格为12288,半角空格为32
  821. ///其他字符半角(33-126)与全角(65281-65374)的对应关系是:均相差65248
  822. ///</remarks>
  823. public static string ToSBC(this string input)
  824. {
  825. //半角转全角:
  826. var c = input.ToCharArray();
  827. for (int i = 0; i < c.Length; i++)
  828. {
  829. if (c[i] == 32)
  830. {
  831. c[i] = (char)12288;
  832. continue;
  833. }
  834. if (c[i] < 127 && c[i] > 32)
  835. {
  836. c[i] = (char)(c[i] + 65248);
  837. }
  838. }
  839. return new string(c);
  840. }
  841. }
  842. }