StringHelper.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  1. using PlcSiemens.Core.Common;
  2. using System.Security.Cryptography;
  3. using System.Text;
  4. using System.Text.RegularExpressions;
  5. namespace PlcSiemens.Core.Extension
  6. {
  7. public static class StringHelper
  8. {
  9. #region 正则表达式
  10. private static readonly Regex WebUrlExpression = new Regex(
  11. @"(http|https)://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?", RegexOptions.Singleline | RegexOptions.Compiled);
  12. private static readonly Regex EmailExpression =
  13. new Regex(@"^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$",
  14. RegexOptions.Singleline | RegexOptions.Compiled);
  15. private static readonly Regex StripHtmlExpression = new Regex("<\\S[^><]*>",
  16. RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.CultureInvariant |
  17. RegexOptions.Compiled);
  18. private static readonly char[] IllegalUrlCharacters =
  19. {
  20. ';', '/', '\\', '?', ':', '@', '&', '=', '+', '$', ',',
  21. '<', '>', '#', '%', '.', '!', '*', '\'', '"', '(', ')', '[', ']', '{', '}', '|', '^', '`', '~', '–', '‘',
  22. '’', '“', '”', '»', '«'
  23. };
  24. #endregion 正则表达式
  25. #region 字符串判断
  26. public static bool IsNullOrEmpty(this string target)
  27. {
  28. return target == null || target.Length <= 0 || string.IsNullOrEmpty(target);
  29. }
  30. /// <summary>是否空或者空白字符串</summary>
  31. public static bool IsNullOrWhiteSpace(this string value)
  32. {
  33. return value == null || value.All(char.IsWhiteSpace);
  34. }
  35. /// <summary> 合法URL </summary>
  36. public static bool IsWebUrl(this string target)
  37. {
  38. return !target.IsNullOrEmpty() && WebUrlExpression.IsMatch(target);
  39. }
  40. /// <summary> 合法邮箱地址 </summary>
  41. public static bool IsEmail(this string target)
  42. {
  43. return !target.IsNullOrEmpty() && EmailExpression.IsMatch(target);
  44. }
  45. #endregion 字符串判断
  46. #region 格式转换
  47. /// <summary> 字符串转Hash </summary>
  48. public static string Hash(this string target)
  49. {
  50. Argument.IsNotEmpty(target, "target");
  51. using (var md5 = MD5.Create())
  52. {
  53. var data = Encoding.Unicode.GetBytes(target);
  54. var hash = md5.ComputeHash(data);
  55. return Convert.ToBase64String(hash);
  56. }
  57. }
  58. /// <summary>字符串转数组 默认UTF8</summary>
  59. public static byte[] GetBytes(this string target, Encoding encoding = null)
  60. {
  61. if (target == null) return null;
  62. if (target == string.Empty) return new byte[0];
  63. if (encoding == null) encoding = Encoding.UTF8;
  64. return encoding.GetBytes(target);
  65. }
  66. /// <summary> 转脚本 </summary>
  67. public static string StripHtml(this string target)
  68. {
  69. return StripHtmlExpression.Replace(target, string.Empty);
  70. }
  71. /// <summary> 转GUID </summary>
  72. public static Guid ToGuid(this string target)
  73. {
  74. var result = Guid.Empty;
  75. if (!string.IsNullOrEmpty(target) && (target.Trim().Length == 22))
  76. {
  77. var encoded = string.Concat(target.Trim().Replace("-", "+").Replace("_", "/"), "==");
  78. try
  79. {
  80. var base64 = Convert.FromBase64String(encoded);
  81. result = new Guid(base64);
  82. }
  83. catch (FormatException)
  84. {
  85. }
  86. }
  87. return result;
  88. }
  89. /// <summary> 字符串转枚举 </summary>
  90. public static T ToEnum<T>(this string target, T defaultValue) where T : IComparable, IFormattable
  91. {
  92. var convertedValue = defaultValue;
  93. if (!string.IsNullOrEmpty(target))
  94. {
  95. try
  96. {
  97. convertedValue = (T)Enum.Parse(typeof(T), target.Trim(), true);
  98. }
  99. catch (ArgumentException)
  100. {
  101. }
  102. }
  103. return convertedValue;
  104. }
  105. /// <summary> 合法URL </summary>
  106. public static string ToLegalUrl(this string target)
  107. {
  108. if (string.IsNullOrEmpty(target))
  109. {
  110. return target;
  111. }
  112. target = target.Trim();
  113. if (target.IndexOfAny(IllegalUrlCharacters) > -1)
  114. {
  115. foreach (var character in IllegalUrlCharacters)
  116. {
  117. target = target.Replace(character.ToString(Constants.CurrentCulture), string.Empty);
  118. }
  119. }
  120. target = target.Replace(" ", "-");
  121. while (target.Contains("--"))
  122. {
  123. target = target.Replace("--", "-");
  124. }
  125. return target;
  126. }
  127. /// <summary> 对URL字符串进行编码 </summary>
  128. public static string UrlEncode(this string target)
  129. {
  130. return target;// HttpUtility.UrlEncode(target);
  131. }
  132. /// <summary> 对URL字符串进行解码 </summary>
  133. public static string UrlDecode(this string target)
  134. {
  135. return target;// HttpUtility.UrlDecode(target);
  136. }
  137. /// <summary> 将字符串最小限度地转换为 HTML 编码的字符串。 </summary>
  138. public static string AttributeEncode(this string target)
  139. {
  140. return target;// HttpUtility.HtmlAttributeEncode(target);
  141. }
  142. /// <summary> 将字符串转换为 HTML 编码的字符串。 </summary>
  143. public static string HtmlEncode(this string target)
  144. {
  145. return target;// HttpUtility.HtmlEncode(target);
  146. }
  147. /// <summary> 将已经为 HTTP 传输进行过 HTML 编码的字符串转换为已解码的字符串。 </summary>
  148. public static string HtmlDecode(this string target)
  149. {
  150. return target;// HttpUtility.HtmlDecode(target);
  151. }
  152. #endregion 格式转换
  153. #region 截取扩展
  154. /// <summary>
  155. /// 截断补.
  156. /// </summary>
  157. public static string WrapAt(this string target, int index)
  158. {
  159. const int dotCount = 3;
  160. Argument.IsNotEmpty(target, "target");
  161. Argument.IsNotNegativeOrZero(index, "index");
  162. return target.Length <= index
  163. ? target
  164. : string.Concat(target.Substring(0, index - dotCount), new string('.', dotCount));
  165. }
  166. /// <summary>确保字符串以指定的另一字符串开始,不区分大小写</summary>
  167. public static string EnsureStart(this string target, string start)
  168. {
  169. if (start.IsNullOrEmpty()) return target;
  170. if (target.IsNullOrEmpty()) return start;
  171. if (target.StartsWith(start, StringComparison.OrdinalIgnoreCase)) return target;
  172. return start + target;
  173. }
  174. /// <summary>确保字符串以指定的另一字符串结束,不区分大小写</summary>
  175. public static string EnsureEnd(this string target, string end)
  176. {
  177. if (end.IsNullOrEmpty()) return target;
  178. if (target.IsNullOrEmpty()) return end;
  179. if (target.EndsWith(end, StringComparison.OrdinalIgnoreCase)) return target;
  180. return target + end;
  181. }
  182. /// <summary>从当前字符串开头移除另一字符串,不区分大小写,循环多次匹配前缀</summary>
  183. public static string TrimStart(this string target, params string[] starts)
  184. {
  185. if (target.IsNullOrEmpty()) return target;
  186. if (starts == null || starts.Length < 1 || string.IsNullOrEmpty(starts[0])) return target;
  187. for (var i = 0; i < starts.Length; i++)
  188. {
  189. if (target.StartsWith(starts[i], StringComparison.OrdinalIgnoreCase))
  190. {
  191. target = target.Substring(starts[i].Length);
  192. if (string.IsNullOrEmpty(target)) break;
  193. // 从头开始
  194. i = -1;
  195. }
  196. }
  197. return target;
  198. }
  199. /// <summary>从当前字符串结尾移除另一字符串,不区分大小写,循环多次匹配后缀</summary>
  200. public static string TrimEnd(this string target, params string[] ends)
  201. {
  202. if (target.IsNullOrEmpty()) return target;
  203. if (ends == null || ends.Length < 1 || string.IsNullOrEmpty(ends[0])) return target;
  204. for (var i = 0; i < ends.Length; i++)
  205. {
  206. if (target.EndsWith(ends[i], StringComparison.OrdinalIgnoreCase))
  207. {
  208. target = target.Substring(0, target.Length - ends[i].Length);
  209. if (string.IsNullOrEmpty(target)) break;
  210. // 从头开始
  211. i = -1;
  212. }
  213. }
  214. return target;
  215. }
  216. /// <summary>从字符串中检索子字符串,在指定头部字符串之后,指定尾部字符串之前</summary>
  217. public static string Substring(this string target, string after, string before = null, int startIndex = 0,
  218. int[] positions = null)
  219. {
  220. if (target.IsNullOrEmpty()) return target;
  221. if (after.IsNullOrEmpty() && before.IsNullOrEmpty()) return target;
  222. var p = -1;
  223. if (!string.IsNullOrEmpty(after))
  224. {
  225. p = target.IndexOf(after, startIndex, StringComparison.Ordinal);
  226. if (p < 0) return null;
  227. p += after.Length;
  228. // 记录位置
  229. if (positions != null && positions.Length > 0) positions[0] = p;
  230. }
  231. if (string.IsNullOrEmpty(before)) return target.Substring(p);
  232. var f = target.IndexOf(before, p >= 0 ? p : startIndex, StringComparison.Ordinal);
  233. if (f < 0) return null;
  234. // 记录位置
  235. if (positions != null && positions.Length > 1) positions[1] = f;
  236. if (p >= 0)
  237. return target.Substring(p, f - p);
  238. return target.Substring(0, f);
  239. }
  240. /// <summary>根据最大长度截取字符串,并允许以指定空白填充末尾</summary>
  241. public static string Cut(this string str, int maxLength, string pad = null)
  242. {
  243. if (str.IsNullOrEmpty() || maxLength <= 0 || str.Length < maxLength) return str;
  244. // 计算截取长度
  245. var len = maxLength;
  246. if (!pad.IsNullOrEmpty()) len -= pad.Length;
  247. if (len <= 0) return pad;
  248. return str.Substring(0, len) + pad;
  249. }
  250. /// <summary>根据最大长度截取字符串(二进制计算长度),并允许以指定空白填充末尾</summary>
  251. /// <remarks>默认采用Default编码进行处理,其它编码请参考本函数代码另外实现</remarks>
  252. /// <param name="str">字符串</param>
  253. /// <param name="maxLength">截取后字符串的最大允许长度,包含后面填充</param>
  254. /// <param name="pad">需要填充在后面的字符串,比如几个圆点</param>
  255. /// <param name="strict">严格模式时,遇到截断位置位于一个字符中间时,忽略该字符,否则包括该字符。默认true</param>
  256. /// <returns></returns>
  257. public static string CutBinary(this string str, int maxLength, string pad = null, bool strict = true)
  258. {
  259. if (string.IsNullOrEmpty(str) || maxLength <= 0 || str.Length < maxLength) return str;
  260. var encoding = Encoding.Default;
  261. var buf = encoding.GetBytes(str);
  262. if (buf.Length < maxLength) return str;
  263. // 计算截取字节长度
  264. var len = maxLength;
  265. if (!string.IsNullOrEmpty(pad)) len -= encoding.GetByteCount(pad);
  266. if (len <= 0) return pad;
  267. // 计算截取字符长度。避免把一个字符劈开
  268. int clen;
  269. while (true)
  270. {
  271. try
  272. {
  273. clen = encoding.GetCharCount(buf, 0, len);
  274. break;
  275. }
  276. catch (DecoderFallbackException)
  277. {
  278. // 发生了回退,减少len再试
  279. len--;
  280. }
  281. }
  282. // 可能过长,修正
  283. if (strict) while (encoding.GetByteCount(str.ToCharArray(), 0, clen) > len) clen--;
  284. return str.Substring(0, clen) + pad;
  285. }
  286. /// <summary>从当前字符串开头移除另一字符串以及之前的部分</summary>
  287. /// <param name="str">当前字符串</param>
  288. /// <param name="starts">另一字符串</param>
  289. /// <returns></returns>
  290. public static string CutStart(this string str, params string[] starts)
  291. {
  292. if (string.IsNullOrEmpty(str)) return str;
  293. if (starts == null || starts.Length < 1 || string.IsNullOrEmpty(starts[0])) return str;
  294. for (var i = 0; i < starts.Length; i++)
  295. {
  296. var p = str.IndexOf(starts[i], StringComparison.Ordinal);
  297. if (p >= 0)
  298. {
  299. str = str.Substring(p + starts[i].Length);
  300. if (string.IsNullOrEmpty(str)) break;
  301. }
  302. }
  303. return str;
  304. }
  305. /// <summary>从当前字符串结尾移除另一字符串以及之后的部分</summary>
  306. /// <param name="str">当前字符串</param>
  307. /// <param name="ends">另一字符串</param>
  308. /// <returns></returns>
  309. public static string CutEnd(this string str, params string[] ends)
  310. {
  311. if (string.IsNullOrEmpty(str)) return str;
  312. if (ends == null || ends.Length < 1 || string.IsNullOrEmpty(ends[0])) return str;
  313. for (var i = 0; i < ends.Length; i++)
  314. {
  315. var p = str.LastIndexOf(ends[i], StringComparison.Ordinal);
  316. if (p >= 0)
  317. {
  318. str = str.Substring(0, p);
  319. if (string.IsNullOrEmpty(str)) break;
  320. }
  321. }
  322. return str;
  323. }
  324. #endregion 截取扩展
  325. #region 匹配查找
  326. /// <summary>忽略大小写的字符串相等比较,判断是否以任意一个待比较字符串相等</summary>
  327. public static bool EqualIgnoreCase(this string target, params string[] strs)
  328. {
  329. return !target.IsNullOrEmpty() &&
  330. strs.Any(item => string.Equals(target, item, StringComparison.OrdinalIgnoreCase));
  331. }
  332. /// <summary>忽略大小写的字符串开始比较,判断是否以任意一个待比较字符串开始</summary>
  333. public static bool StartsWithIgnoreCase(this string target, params string[] strs)
  334. {
  335. return !target.IsNullOrEmpty() &&
  336. strs.Any(item => target.StartsWith(item, StringComparison.OrdinalIgnoreCase));
  337. }
  338. /// <summary>忽略大小写的字符串结束比较,判断是否以任意一个待比较字符串结束</summary>
  339. public static bool EndsWithIgnoreCase(this string target, params string[] strs)
  340. {
  341. return !target.IsNullOrEmpty() &&
  342. strs.Any(item => target.EndsWith(item, StringComparison.OrdinalIgnoreCase));
  343. }
  344. public static int GetHashcode2(this string s)
  345. {
  346. if (string.IsNullOrEmpty(s)) return 0;
  347. unchecked
  348. {
  349. int hash = 23;
  350. foreach (char c in s)
  351. {
  352. hash = (hash << 5) - hash + c;
  353. }
  354. if (hash < 0)
  355. {
  356. hash = Math.Abs(hash);
  357. }
  358. return hash;
  359. }
  360. }
  361. #endregion 匹配查找
  362. #region 分隔
  363. /// <summary>拆分字符串,过滤空格,无效时返回空数组</summary>
  364. public static string[] Split(this string target, params string[] separators)
  365. {
  366. if (target.IsNullOrEmpty()) return new string[0];
  367. if (separators == null || separators.Length < 1 || separators.Length == 1 && separators[0].IsNullOrEmpty())
  368. separators = new[] { ",", ";" };
  369. return target.Split(separators, StringSplitOptions.RemoveEmptyEntries);
  370. }
  371. /// <summary>拆分字符串成为整型数组,默认逗号分号分隔,无效时返回空数组</summary>
  372. public static int[] SplitAsInt(this string target, params string[] separators)
  373. {
  374. if (target.IsNullOrEmpty()) return new int[0];
  375. if (separators == null || separators.Length < 1) separators = new[] { ",", ";" };
  376. var ss = target.Split(separators, StringSplitOptions.RemoveEmptyEntries);
  377. var list = new List<int>();
  378. foreach (var item in ss)
  379. {
  380. int id;
  381. if (!int.TryParse(item.Trim(), out id)) continue;
  382. list.Add(id);
  383. }
  384. return list.ToArray();
  385. }
  386. /// <summary>拆分字符串成为名值字典。逗号分号分组,等号分隔</summary>
  387. public static IDictionary<string, string> SplitAsDictionary(this string target, string nameValueSeparator = "=",
  388. params string[] separators)
  389. {
  390. var dic = new Dictionary<string, string>();
  391. if (target.IsNullOrWhiteSpace()) return dic;
  392. if (nameValueSeparator.IsNullOrEmpty()) nameValueSeparator = "=";
  393. if (separators == null || separators.Length < 1) separators = new[] { ",", ";" };
  394. var ss = target.Split(separators, StringSplitOptions.RemoveEmptyEntries);
  395. if (ss.Length < 1) return null;
  396. foreach (var item in ss)
  397. {
  398. var p = item.IndexOf(nameValueSeparator, StringComparison.Ordinal);
  399. // 在前后都不行
  400. if (p <= 0 || p >= item.Length - 1) continue;
  401. var key = item.Substring(0, p).Trim();
  402. dic[key] = item.Substring(p + nameValueSeparator.Length).Trim();
  403. }
  404. return dic;
  405. }
  406. #endregion 分隔
  407. #region 功能扩展
  408. /// <summary> 安全字符串 </summary>
  409. public static string NullSafe(this string target)
  410. {
  411. return (target ?? string.Empty).Trim();
  412. }
  413. /// <summary> 字符串格式化 </summary>
  414. public static string FormatWith(this string target, params object[] args)
  415. {
  416. Argument.IsNotEmpty(target, "target");
  417. for (var i = 0; i < args.Length; i++)
  418. {
  419. if (args[i] is DateTime)
  420. {
  421. if (target.Contains("{" + i + "}")) args[i] = ((DateTime)args[i]).ToFullString();
  422. }
  423. }
  424. return string.Format(target, args);
  425. }
  426. public static string Replace(this string target, ICollection<string> oldValues, string newValue)
  427. {
  428. oldValues.ForEach(oldValue => target = target.Replace(oldValue, newValue));
  429. return target;
  430. }
  431. #endregion 功能扩展
  432. #region 语音播放
  433. /// <summary>调用语音引擎说出指定话</summary>
  434. /// <param name="value"></param>
  435. public static void Speak(this string value)
  436. {
  437. //Speecher.Speak(value);
  438. }
  439. /// <summary>调用语音引擎说出指定话</summary>
  440. /// <param name="value"></param>
  441. public static void SpeakAsync(this string value)
  442. {
  443. //Speecher.SpeakAsync(value);
  444. }
  445. #endregion 语音播放
  446. #region LD编辑距离算法
  447. /// <summary>编辑距离搜索,从词组中找到最接近关键字的若干匹配项</summary>
  448. /// <remarks>
  449. /// 算法代码由@Aimeast 独立完成。http://www.cnblogs.com/Aimeast/archive/2011/09/05/2167844.html
  450. /// </remarks>
  451. /// <param name="key">关键字</param>
  452. /// <param name="words">词组</param>
  453. /// <returns></returns>
  454. public static string[] LevenshteinSearch(string key, string[] words)
  455. {
  456. if (IsNullOrWhiteSpace(key)) return new string[0];
  457. var keys = key.Split(new[] { ' ', ' ' }, StringSplitOptions.RemoveEmptyEntries);
  458. foreach (var item in keys)
  459. {
  460. var maxDist = (item.Length - 1) / 2;
  461. var q = from str in words
  462. where item.Length <= str.Length
  463. && Enumerable.Range(0, maxDist + 1)
  464. .Any(dist =>
  465. {
  466. return Enumerable.Range(0, Math.Max(str.Length - item.Length - dist + 1, 0))
  467. .Any(
  468. f =>
  469. {
  470. return LevenshteinDistance(item, str.Substring(f, item.Length + dist)) <=
  471. maxDist;
  472. });
  473. })
  474. orderby str
  475. select str;
  476. words = q.ToArray();
  477. }
  478. return words;
  479. }
  480. /// <summary>编辑距离</summary>
  481. /// <remarks>
  482. /// 又称Levenshtein距离(也叫做Edit Distance),是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。
  483. /// 许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
  484. /// 算法代码由@Aimeast 独立完成。http://www.cnblogs.com/Aimeast/archive/2011/09/05/2167844.html
  485. /// </remarks>
  486. /// <param name="str1"></param>
  487. /// <param name="str2"></param>
  488. /// <returns></returns>
  489. public static int LevenshteinDistance(string str1, string str2)
  490. {
  491. var n = str1.Length;
  492. var m = str2.Length;
  493. var C = new int[n + 1, m + 1];
  494. int i, j, x, y, z;
  495. for (i = 0; i <= n; i++)
  496. C[i, 0] = i;
  497. for (i = 1; i <= m; i++)
  498. C[0, i] = i;
  499. for (i = 0; i < n; i++)
  500. for (j = 0; j < m; j++)
  501. {
  502. x = C[i, j + 1] + 1;
  503. y = C[i + 1, j] + 1;
  504. if (str1[i] == str2[j])
  505. z = C[i, j];
  506. else
  507. z = C[i, j] + 1;
  508. C[i + 1, j + 1] = Math.Min(Math.Min(x, y), z);
  509. }
  510. return C[n, m];
  511. }
  512. #endregion LD编辑距离算法
  513. #region LCS算法
  514. /// <summary>最长公共子序列搜索,从词组中找到最接近关键字的若干匹配项</summary>
  515. /// <remarks>
  516. /// 算法代码由@Aimeast 独立完成。http://www.cnblogs.com/Aimeast/archive/2011/09/05/2167844.html
  517. /// </remarks>
  518. /// <param name="key"></param>
  519. /// <param name="words"></param>
  520. /// <returns></returns>
  521. public static string[] LCSSearch(string key, string[] words)
  522. {
  523. if (IsNullOrWhiteSpace(key) || words == null || words.Length == 0) return new string[0];
  524. var keys = key
  525. .Split(new[] { ' ', '\u3000' }, StringSplitOptions.RemoveEmptyEntries)
  526. .OrderBy(s => s.Length)
  527. .ToArray();
  528. //var q = from sentence in items.AsParallel()
  529. var q = from word in words
  530. let MLL = LCSDistance(word, keys)
  531. where MLL >= 0
  532. orderby (MLL + 0.5) / word.Length, word
  533. select word;
  534. return q.ToArray();
  535. }
  536. /// <summary>
  537. /// 最长公共子序列问题是寻找两个或多个已知数列最长的子序列。
  538. /// 一个数列 S,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。
  539. /// The longest common subsequence (LCS) problem is to find the longest subsequence common to all sequences in a set of
  540. /// sequences (often just two). Note that subsequence is different from a substring, see substring vs. subsequence. It
  541. /// is a classic computer science problem, the basis of diff (a file comparison program that outputs the differences
  542. /// between two files), and has applications in bioinformatics.
  543. /// </summary>
  544. /// <remarks>
  545. /// 算法代码由@Aimeast 独立完成。http://www.cnblogs.com/Aimeast/archive/2011/09/05/2167844.html
  546. /// </remarks>
  547. /// <param name="word"></param>
  548. /// <param name="keys">多个关键字。长度必须大于0,必须按照字符串长度升序排列。</param>
  549. /// <returns></returns>
  550. public static int LCSDistance(string word, string[] keys)
  551. {
  552. var sLength = word.Length;
  553. var result = sLength;
  554. var flags = new bool[sLength];
  555. var C = new int[sLength + 1, keys[keys.Length - 1].Length + 1];
  556. //int[,] C = new int[sLength + 1, words.Select(s => s.Length).Max() + 1];
  557. foreach (var key in keys)
  558. {
  559. var wLength = key.Length;
  560. int first = 0, last = 0;
  561. int i = 0, j = 0, LCS_L;
  562. //foreach 速度会有所提升,还可以加剪枝
  563. for (i = 0; i < sLength; i++)
  564. for (j = 0; j < wLength; j++)
  565. if (word[i] == key[j])
  566. {
  567. C[i + 1, j + 1] = C[i, j] + 1;
  568. if (first < C[i, j])
  569. {
  570. last = i;
  571. first = C[i, j];
  572. }
  573. }
  574. else
  575. C[i + 1, j + 1] = Math.Max(C[i, j + 1], C[i + 1, j]);
  576. LCS_L = C[i, j];
  577. if (LCS_L <= wLength >> 1)
  578. return -1;
  579. while (i > 0 && j > 0)
  580. {
  581. if (C[i - 1, j - 1] + 1 == C[i, j])
  582. {
  583. i--;
  584. j--;
  585. if (!flags[i])
  586. {
  587. flags[i] = true;
  588. result--;
  589. }
  590. first = i;
  591. }
  592. else if (C[i - 1, j] == C[i, j])
  593. i--;
  594. else // if (C[i, j - 1] == C[i, j])
  595. j--;
  596. }
  597. if (LCS_L <= (last - first + 1) >> 1)
  598. return -1;
  599. }
  600. return result;
  601. }
  602. #endregion LCS算法
  603. #region 执行命令行
  604. ///// <summary>以隐藏窗口执行命令行</summary>
  605. ///// <param name="cmd">文件名</param>
  606. ///// <param name="arguments">命令参数</param>
  607. ///// <param name="msWait">等待毫秒数</param>
  608. ///// <param name="output">进程输出内容。默认为空时输出到日志</param>
  609. ///// <param name="onExit">进程退出时执行</param>
  610. ///// <returns>进程退出代码</returns>
  611. //public static int Run(this string cmd, string arguments = null, int msWait = 0, Action<string> output = null, Action<Process> onExit = null)
  612. //{
  613. // if (XTrace.Debug) XTrace.WriteLine("Run {0} {1} {2}", cmd, arguments, msWait);
  614. // var p = new Process();
  615. // var si = p.StartInfo;
  616. // si.FileName = cmd;
  617. // si.Arguments = arguments;
  618. // si.WindowStyle = ProcessWindowStyle.Hidden;
  619. // // 对于控制台项目,这里需要捕获输出
  620. // if (msWait > 0)
  621. // {
  622. // si.RedirectStandardOutput = true;
  623. // si.RedirectStandardError = true;
  624. // si.UseShellExecute = false;
  625. // if (output != null)
  626. // {
  627. // p.OutputDataReceived += (s, e) => output(e.Data);
  628. // p.ErrorDataReceived += (s, e) => output(e.Data);
  629. // }
  630. // else if (HouDa.Runtime.IsConsole)
  631. // {
  632. // p.OutputDataReceived += (s, e) => XTrace.WriteLine(e.Data);
  633. // p.ErrorDataReceived += (s, e) => XTrace.Current.Error(e.Data);
  634. // }
  635. // }
  636. // if (onExit != null) p.Exited += (s, e) => onExit(s as Process);
  637. // p.Start();
  638. // if (msWait > 0 && (output != null || HouDa.Runtime.IsConsole))
  639. // {
  640. // p.BeginOutputReadLine();
  641. // p.BeginErrorReadLine();
  642. // }
  643. // if (msWait <= 0) return -1;
  644. // // 如果未退出,则不能拿到退出代码
  645. // if (!p.WaitForExit(msWait)) return -1;
  646. // return p.ExitCode;
  647. //}
  648. #endregion 执行命令行
  649. }
  650. }