You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

580 lines
25 KiB

  1. using Apewer.Internals;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Data;
  5. using System.IO;
  6. using System.Runtime.CompilerServices;
  7. using System.Text;
  8. using System.Text.RegularExpressions;
  9. namespace Apewer
  10. {
  11. /// <summary>文本实用工具。</summary>
  12. public class TextUtility
  13. {
  14. /// <summary>UTF-8 BOM。</summary>
  15. public static byte[] Bom { get => new byte[] { 0xEF, 0xBB, 0xBF }; }
  16. /// <summary>CRLF。</summary>
  17. public static readonly string CRLF = "\r\n";
  18. /// <summary>LF。</summary>
  19. public static readonly string LF = "\n";
  20. /// <summary>空文本。</summary>
  21. public const string EmptyString = Constant.EmptyString;
  22. /// <summary>合并为字符串。</summary>
  23. public static string Merge(params object[] cells) => Join(null, cells as IEnumerable<object>);
  24. /// <summary>合并为字符串。</summary>
  25. public static string Merge(IEnumerable<object> cells) => Join(null, cells as IEnumerable<object>);
  26. /// <summary>合并为字符串。</summary>
  27. public static string Join(string separator, params object[] cells) => Join(separator, (IEnumerable<object>)cells);
  28. /// <summary>合并为字符串。</summary>
  29. public static string Join(string separator, IEnumerable<object> cells)
  30. {
  31. if (cells == null) return Constant.EmptyString;
  32. var sb = new StringBuilder();
  33. var first = true;
  34. var hasSeparator = !string.IsNullOrEmpty(separator);
  35. foreach (var cell in cells)
  36. {
  37. // if (cell == null) continue;
  38. if (!first && hasSeparator) sb.Append(separator);
  39. first = false;
  40. if (cell == null) continue;
  41. var text = (cell is string) ? (string)cell : cell.ToString();
  42. if (string.IsNullOrEmpty(text)) continue;
  43. sb.Append(cell.ToString());
  44. }
  45. var result = sb.ToString();
  46. return result;
  47. }
  48. /// <summary>重复指定子字符串,直到达到指定长度。</summary>
  49. /// <param name="cell">子字符串。</param>
  50. /// <param name="length">目标字符串的长度。</param>
  51. public static string CopyChar(string cell, int length) => TextGenerator.CopyChar(cell, length);
  52. /// <summary>获取指定长的的空格。</summary>
  53. public static string Space(int length) => TextGenerator.Space(length);
  54. /// <summary>将文本以转换为字节数组。默认 Encoding 为 UTF-8。</summary>
  55. public static byte[] ToBinary(string text, Encoding encoding = null) => TextConverter.ToBinary(text, encoding ?? Encoding.UTF8);
  56. /// <summary>将字节数组转换为文本。默认 Encoding 为 UTF-8。</summary>
  57. public static string FromBinary(byte[] bytes, Encoding encoding = null)
  58. {
  59. if (bytes == null) return Constant.EmptyString;
  60. if (bytes.LongLength < 1L) return Constant.EmptyString;
  61. try
  62. {
  63. if (encoding == null) return FromBinary(bytes, Encoding.UTF8);
  64. return encoding.GetString(bytes);
  65. }
  66. catch { return Constant.EmptyString; }
  67. }
  68. /// <summary>将明文文本以 UTF-8 转换为 Base64 文本。</summary>
  69. public static string ToBase64(string plain) => TextConverter.ToBase64(plain);
  70. /// <summary>将 Base64 文本以 UTF-8 转换为明文文本。</summary>
  71. public static string FromBase64(string cipher) => TextConverter.FromBase64(cipher);
  72. /// <summary>byte -> plain</summary>
  73. public static string EncodeByte(byte @byte) => TextConverter.EncodeByte(@byte);
  74. /// <summary>binary -> hex plain</summary>
  75. public static string EncodeBinary(byte[] bytes)
  76. {
  77. return TextConverter.EncodeBinary(bytes);
  78. }
  79. /// <summary>hex text -> plain</summary>
  80. public static string EncodeText(string text, bool delimiter = false) => TextConverter.EncodeText(text, delimiter);
  81. /// <summary>hex plain -> binary</summary>
  82. public static byte[] DecodeBinary(string plain) => TextConverter.DecodeBinary(plain);
  83. /// <summary>hex plain -> text</summary>
  84. public static string DecodeText(string plain) => TextConverter.DecodeText(plain);
  85. /// <summary>将字节数组格式化为字符串。</summary>
  86. public static string FormatX2(params byte[] bytes) => TextConverter.FormatX2(bytes);
  87. /// <summary>获取单精度浮点对象。</summary>
  88. public static Single GetFloat(char origin) => TextConverter.GetSingle(origin.ToString());
  89. /// <summary>获取单精度浮点对象。</summary>
  90. public static Single GetFloat(string origin) => TextConverter.GetSingle(origin);
  91. /// <summary>获取单精度浮点对象。</summary>
  92. public static Single GetSingle(char origin) => TextConverter.GetSingle(origin.ToString());
  93. /// <summary>获取单精度浮点对象。</summary>
  94. public static Single GetSingle(string origin) => TextConverter.GetSingle(origin);
  95. /// <summary>获取双精度浮点对象。</summary>
  96. public static Double GetDouble(char origin) => TextConverter.GetDouble(origin.ToString());
  97. /// <summary>获取双精度浮点对象。</summary>
  98. public static Double GetDouble(string origin) => TextConverter.GetDouble(origin);
  99. /// <summary>获取 Decimal 对象。</summary>
  100. public static decimal GetDecimal(char origin) => TextConverter.GetDecimal(origin.ToString());
  101. /// <summary>获取 Decimal 对象。</summary>
  102. public static decimal GetDecimal(string origin) => TextConverter.GetDecimal(origin);
  103. /// <summary>获取 Int16 对象。</summary>
  104. public static Int16 GetInt16(char origin) => TextConverter.GetInt16(origin.ToString());
  105. /// <summary>获取 Int16 对象。</summary>
  106. public static Int16 GetInt16(string origin) => TextConverter.GetInt16(origin);
  107. /// <summary>获取 Int32 对象。</summary>
  108. public static Int32 GetInt32(char origin) => TextConverter.GetInt32(origin.ToString());
  109. /// <summary>获取 Int32 对象。</summary>
  110. public static Int32 GetInt32(string origin) => TextConverter.GetInt32(origin);
  111. /// <summary>获取 Int64 对象。</summary>
  112. public static Int64 GetInt64(char origin) => TextConverter.GetInt64(origin.ToString());
  113. /// <summary>获取 Int64 对象。</summary>
  114. public static Int64 GetInt64(string origin) => TextConverter.GetInt64(origin);
  115. /// <summary>为字符串前添加字符“0”。</summary>
  116. /// <param name="origin">原字符串,内容应为整数、小数或十六进制数,若格式不符则返回原字符串。</param>
  117. /// <param name="length">新字符串的长度,若大于原数字长度,则不添加额外的“0”。</param>
  118. public static string PreZero(string origin, int length = 0) => TextModifier.PreZero(origin, length);
  119. /// <summary>删除字符串前额外的字符“0”。</summary>
  120. public static string RemoveZero(string text) => TextModifier.RemoveZero(text);
  121. /// <summary>替换父字符串中的子字符串。</summary>
  122. /// <param name="parent">父字符串。</param>
  123. /// <param name="new">新子字符串,保留大小写。</param>
  124. /// <param name="old">原子字符串。</param>
  125. /// <param name="ignoreCase">查找时是否忽略父字符串和原子字符串大小写。</param>
  126. /// <returns>替换后的父字符串。</returns>
  127. public static string Replace(string parent, string old, string @new, bool ignoreCase = false) => TextModifier.Replace(parent, old, @new, ignoreCase);
  128. /// <summary>修复文本后缀。默认用于修复 Windows 目录路径。</summary>
  129. /// <param name="origin">原文本。</param>
  130. /// <param name="include">True:追加指定后缀;False:去除指定后缀。</param>
  131. /// <param name="foot">后缀文本。</param>
  132. public static string AssureEnds(string origin, bool include = true, string foot = "\\") => TextModifier.AssureEnds(origin, include, foot);
  133. /// <summary>用单字符作为分隔符拆分文本。</summary>
  134. public static string[] Split(string text, char separator)
  135. {
  136. if (text == null) return new string[0];
  137. if (text.Length < 1) return new string[] { "" };
  138. if ((object)separator == null) return new string[] { text };
  139. return text.Split(separator);
  140. }
  141. /// <summary>用字符串作为分隔符拆分文本。</summary>
  142. public static string[] Split(string text, string separator)
  143. {
  144. if (text == null) return new string[0];
  145. if (text.Length < 1) return new string[] { "" };
  146. if (string.IsNullOrEmpty(separator)) return new string[] { text };
  147. if (separator.Length > text.Length) return new string[] { text };
  148. var list = new List<string>();
  149. var position = 0;
  150. var total = text.Length;
  151. var length = separator.Length;
  152. var cell = new StringBuilder();
  153. while (position < total)
  154. {
  155. var read = null as string;
  156. if (position + length < total) read = text.Substring(position, length);
  157. else read = text.Substring(position);
  158. if (read == separator)
  159. {
  160. if (cell.Length > 0)
  161. {
  162. list.Add(cell.ToString());
  163. // cell.Clear();
  164. cell = new StringBuilder();
  165. }
  166. else
  167. {
  168. list.Add("");
  169. }
  170. position += length;
  171. }
  172. else
  173. {
  174. cell.Append((char)text[position]);
  175. position += 1;
  176. }
  177. if (position >= total)
  178. {
  179. list.Add(cell.ToString());
  180. }
  181. }
  182. var array = list.ToArray();
  183. return array;
  184. }
  185. /// <summary>用多个分隔符拆分文本。</summary>
  186. public static string[] Split(string text, params char[] separators)
  187. {
  188. if (text == null) return new string[0];
  189. if (text.Length < 1) return new string[] { "" };
  190. if (separators == null || separators.Length < 1) return new string[] { text };
  191. if (separators.Length == 1) return Split(text, separators[0]);
  192. var list = new List<string>();
  193. var separatorsText = new string(separators);
  194. var sb = new StringBuilder();
  195. foreach (var c in text)
  196. {
  197. if (separatorsText.IndexOf(c) >= 0)
  198. {
  199. list.Add(sb.ToString());
  200. //sb.Clear();
  201. sb = new StringBuilder();
  202. continue;
  203. }
  204. sb.Append(c);
  205. }
  206. list.Add(sb.ToString());
  207. #if !NET20
  208. sb.Clear();
  209. #endif
  210. return list.ToArray();
  211. }
  212. /// <summary>移除字符串前后的空白。</summary>
  213. /// <param name="origin">原始字符串。</param>
  214. public static string Trim(string origin)
  215. {
  216. return TextModifier.Trim(origin);
  217. }
  218. /// <summary>移除字符串前后的空白。</summary>
  219. /// <param name="origin">原始字符串。</param>
  220. /// <param name="allCases">所有情况,全角空格将被去除。</param>
  221. public static string Trim(string origin, bool allCases) => TextModifier.Trim(origin, allCases);
  222. /// <summary>防注入处理,去除会引发代码注入的字符。可限定字符串长度。</summary>
  223. public static string AntiInject(string text, int length, params char[] blacklist) => TextModifier.AntiInject(text, length, blacklist);
  224. /// <summary>防注入处理,去除会引发代码注入的字符。可限定字符串长度。</summary>
  225. public static string AntiInject(string text, int length, IEnumerable<char> blacklist) => TextModifier.AntiInject(text, length, blacklist);
  226. /// <summary>防注入处理,去除会引发代码注入的字符。可限定字符串长度。</summary>
  227. public static string AntiInject(string text, int length = -1, string blacklist = Constant.InjectDefaultBlackList)
  228. {
  229. return TextModifier.AntiInject(text, length, blacklist == null ? null : blacklist.ToCharArray());
  230. }
  231. /// <summary>剪取文本内容,若指定头部为空则从原文本首部起,若指定尾部为空则至原文本末尾。</summary>
  232. public static string Cut(string origin, string head = null, string foot = null) => TextModifier.Cut(origin, head, foot);
  233. /// <summary>比较两个字符串的相似度。返回值大于 0,小于等于 1。</summary>
  234. /// <param name="arg1"></param>
  235. /// <param name="arg2"></param>
  236. /// <returns></returns>
  237. public static double Similarity(string arg1, string arg2) => Levenshtein.Compute(arg1, arg2).Rate;
  238. /// <summary>判断对象为 Null、空字符串或空白字符串。</summary>
  239. public static bool IsEmpty(string text) => TextVerifier.IsEmpty(text);
  240. /// <summary>判断对象为含有内容的字符串。</summary>
  241. public static bool NotEmpty(string text) => !TextVerifier.IsEmpty(text);
  242. /// <summary>判断对象为 Null、空字符串或无实际内容的字符串。</summary>
  243. public static bool IsBlank(string text, bool allCases = false) => TextVerifier.IsBlank(text, allCases);
  244. /// <summary>判断对象为含有实际内容的字符串。</summary>
  245. public static bool NotBlank(string text, bool allCases = false) => !TextVerifier.IsBlank(text, allCases);
  246. /// <summary>生成新的 GUID(不带连字符、小写)。</summary>
  247. public static string NewGuid(bool hyphenation = false, bool lower = true)
  248. {
  249. var guid = Guid.NewGuid();
  250. if (!hyphenation && lower) return guid.ToString("n");
  251. var text = Guid.NewGuid().ToString();
  252. if (lower) text = text.ToLower();
  253. else text = text.ToUpper();
  254. if (!hyphenation) text = text.Replace("-", "");
  255. return text;
  256. }
  257. /// <summary>生成随机字符串,出现的字符由字符池指定,默认池包含数字和字母。</summary>
  258. /// <param name="pool">字符池,字符池中每个字符在随机字符串中出现的概率约等。</param>
  259. /// <param name="length">随机字符串的长度。</param>
  260. public static string Random(int length, string pool = "0123456789abcdefghijklmnopqrstuvwxyz") => RandomHelper.RandomCustom(pool, length);
  261. /// <summary>对字符串列表去重。指定 valid 参数时将去除 NULL、空字符串和空白字符串。</summary>
  262. public static List<string> Distinct(IEnumerable<string> origin, bool valid = false) => TextModifier.Distinct(origin, valid);
  263. /// <summary>约束字符串长度范围,超出的部分将被截取去除。</summary>
  264. public static string RestrictLength(string origin, int length) => TextModifier.RestrictLength(origin, length);
  265. /// <summary>约束字符串长度为 32,超出的部分将被截取去除。</summary>
  266. public static string Restrict32(string origin) => TextModifier.RestrictLength(origin, 32);
  267. /// <summary>约束字符串长度为 255,超出的部分将被截取去除。</summary>
  268. public static string Restrict255(string origin) => TextModifier.RestrictLength(origin, 255);
  269. /// <summary>约束字符串长度为 2000,超出的部分将被截取去除。</summary>
  270. public static string Restrict2000(string origin) => TextModifier.RestrictLength(origin, 2000);
  271. /// <summary>约束字符串中的字符,不允许的字符将被去除。</summary>
  272. public static string RestrictCharacters(string origin, params char[] allowable) => TextModifier.RestrictCharacters(origin, new string(allowable));
  273. /// <summary>约束字符串中的字符,不允许的字符将被去除。</summary>
  274. public static string RestrictCharacters(string origin, string allowable) => TextModifier.RestrictCharacters(origin, allowable);
  275. /// <summary>约束字符串中的字符,只保留字母。</summary>
  276. public static string RestrictLetters(string origin) => TextModifier.RestrictCharacters(origin, Constant.LetterCollection);
  277. /// <summary>约束字符串中的字符,只保留数字。</summary>
  278. public static string RestrictNumeric(string origin) => TextModifier.RestrictCharacters(origin, Constant.NumberCollection);
  279. /// <summary>约束字符串,只保留 GUID 可能出现的字符,根据连字符限定长度为 32 或 36。</summary>
  280. public static string RestrictGuid(string origin) => TextModifier.RestrictGuid(origin);
  281. /// <summary>返回此字符串的安全键副本,只保留数据记录主键中可能出现的字符,默认限制长度为 255 字符。</summary>
  282. public static string SafeKey(string text, int maxLength = 255)
  283. {
  284. if (string.IsNullOrEmpty(text)) return Constant.EmptyString;
  285. var input = ToLower(text);
  286. var max = maxLength;
  287. if (max < 1 || max > input.Length) max = input.Length;
  288. var sb = new StringBuilder();
  289. var total = input.Length;
  290. var length = 0;
  291. for (var i = 0; i < total; i++)
  292. {
  293. var c = input[i];
  294. if (Constant.KeyCollection.IndexOf(c) < 0) continue;
  295. sb.Append(c);
  296. length += 1;
  297. if (length >= max) break;
  298. }
  299. var result = sb.ToString();
  300. if (result.Length > max) result = result.Substring(0, max);
  301. return result;
  302. }
  303. /// <summary>追加字符串。</summary>
  304. public static StringBuilder Append(StringBuilder builder, params object[] cells)
  305. {
  306. if (builder != null) builder.Append(Join(null, cells));
  307. return builder;
  308. }
  309. /// <summary>对 URL 编码。</summary>
  310. public static string EncodeUrl(string plain)
  311. {
  312. return UrlEncoding.Encode(plain);
  313. }
  314. /// <summary>对 URL 解码。</summary>
  315. public static string DecodeUrl(string escaped)
  316. {
  317. return UrlEncoding.Decode(escaped);
  318. }
  319. /// <summary>返回此字符串转换为小写形式的副本。</summary>
  320. public static string ToLower(string text)
  321. {
  322. if (text == null) return null;
  323. else if (text.Length < 1) return EmptyString;
  324. else return text.ToLower();
  325. }
  326. /// <summary>返回此字符串转换为大写形式的副本。</summary>
  327. public static string ToUpper(string text)
  328. {
  329. if (text == null) return null;
  330. else if (text.Length < 1) return EmptyString;
  331. else return text.ToUpper();
  332. }
  333. /// <summary>检查中国手机号码,包含 13x、14x、15x、16x、17x、18x 和 19x 号段。</summary>
  334. public static bool IsPhone(string phone)
  335. {
  336. if (string.IsNullOrEmpty(phone)) return false;
  337. var regex = new Regex(@"^(13|14|15|16|17|18|19)\d{9}$", RegexOptions.None);
  338. var match = regex.Match(phone);
  339. return match.Success;
  340. }
  341. /// <summary>渲染 Markdown 文本为 HTML 文本。</summary>
  342. public static string RenderMarkdown(string markdown) => MarkdownSharp.Demo.ToHtml(markdown);
  343. /// <summary>合并用于启动进程的参数。</summary>
  344. public static string MergeProcessArgument(params object[] args)
  345. {
  346. // var special = " \"\n\r\b\t\f";
  347. var list = new List<string>();
  348. if (args != null)
  349. {
  350. foreach (var i in args)
  351. {
  352. var arg = null as string;
  353. if (i != null)
  354. {
  355. if (i is string) arg = i as string;
  356. else arg = i.ToString();
  357. }
  358. if (string.IsNullOrEmpty(arg))
  359. {
  360. list.Add("\"\"");
  361. continue;
  362. }
  363. if (arg.Contains(" ") || arg.Contains("\""))
  364. {
  365. list.Add(Merge("\"", arg.Replace("\"", "\\\""), "\""));
  366. continue;
  367. }
  368. list.Add(arg);
  369. }
  370. }
  371. var result = Join(" ", list.ToArray());
  372. return result;
  373. }
  374. /// <summary>合并用于启动进程的参数。</summary>
  375. private static string MergeProcessArgument_2(params object[] args)
  376. {
  377. if (args == null) return "";
  378. if (args.Length < 1) return "";
  379. var sb = new StringBuilder();
  380. for (var i = 0; i < args.Length; i++)
  381. {
  382. if (i > 0) sb.Append(" ");
  383. var arg = null as string;
  384. if (args[i] != null)
  385. {
  386. if (args[i] is string) arg = args[i] as string;
  387. else arg = args[i].ToString();
  388. }
  389. if (arg.IsEmpty())
  390. {
  391. sb.Append("\"\"");
  392. continue;
  393. }
  394. // var special = " \"\n\r\b\t\f";
  395. var special = " \"";
  396. if (arg.IndexOfAny(special.ToCharArray()) < 0)
  397. {
  398. sb.Append(arg);
  399. }
  400. else
  401. {
  402. sb.Append("\"");
  403. if (arg.NotEmpty())
  404. {
  405. foreach (var c in arg)
  406. {
  407. switch (c)
  408. {
  409. case '"':
  410. sb.Append("\\\"");
  411. break;
  412. // case '\n':
  413. // sb.Append("\\n");
  414. // break;
  415. // case '\r':
  416. // sb.Append("\\r");
  417. // break;
  418. // case '\b':
  419. // sb.Append("\\b");
  420. // break;
  421. // case '\t':
  422. // sb.Append("\\t");
  423. // break;
  424. default:
  425. sb.Append(c);
  426. break;
  427. }
  428. }
  429. }
  430. sb.Append("\"");
  431. }
  432. }
  433. var result = sb.ToString();
  434. return result;
  435. }
  436. /// <summary>字符串仅使用英文。可指定空字符串的返回值。</summary>
  437. public static bool IsEnglish(string text, bool ifEmpty = true)
  438. {
  439. if (string.IsNullOrEmpty(text)) return ifEmpty;
  440. var trim = text.Trim();
  441. if (string.IsNullOrEmpty(trim)) return ifEmpty;
  442. return Regex.IsMatch(trim, "(^[0-9a-zA-Z_ -;:,~!@#%&=<>~\\(\\)\\.\\$\\^\\`\\'\\\"\\&\\{\\}\\[\\]\\|\\*\\+\\?]{0,80}$)");
  443. }
  444. /// <summary>转换文本为驼峰形式。</summary>
  445. public static string ToCamel(string text)
  446. {
  447. if (string.IsNullOrEmpty(text) || !char.IsUpper(text[0])) return text;
  448. var chars = text.ToCharArray();
  449. for (int i = 0; i < chars.Length; i++)
  450. {
  451. if (i == 1 && !char.IsUpper(chars[i]))
  452. {
  453. break;
  454. }
  455. bool hasNext = (i + 1) < chars.Length;
  456. if (i > 0 && hasNext)
  457. {
  458. // if the next character is a space, which is not considered uppercase
  459. // (otherwise we wouldn't be here...)
  460. // we want to ensure that the following:
  461. // 'FOO bar' is rewritten as 'foo bar', and not as 'foO bar'
  462. // The code was written in such a way that the first word in uppercase
  463. // ends when if finds an uppercase letter followed by a lowercase letter.
  464. // now a ' ' (space, (char)32) is considered not upper
  465. // but in that case we still want our current character to become lowercase
  466. var nextChar = chars[i + 1];
  467. if (!char.IsUpper(nextChar))
  468. {
  469. if (char.IsSeparator(nextChar)) chars[i] = char.ToLowerInvariant(chars[i]);
  470. break;
  471. }
  472. }
  473. chars[i] = char.ToLowerInvariant(chars[i]);
  474. }
  475. return new string(chars);
  476. }
  477. }
  478. }