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.

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