using Apewer.Internals; using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; namespace Apewer { /// 文本实用工具。 public static class TextUtility { const string BlankChars = "  \n\r\t\f\b\a"; // 在 IsBlank 和 Trim 中视为空白的字符。 const string LineFeed = "\r\n"; // 换行符,由 ASCII 13 和 ASCII 10 组成。 const string SpaceDbc = " "; const string SpaceSbc = " "; const string LucidChars = "3456789acefhknpstwxyz"; const string KeyChars = "0123456789abcdefghijklmnopqrstuvwxyz"; const string HexChars = "0123456789abcdef"; const string NumericChars = "0123456789"; const string LowerChars = "abcdefghijklmnopqrstuvwxyz"; const string UpperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const string LetterChars = LowerChars + UpperChars; /// UTF-8 BOM。 public static byte[] Bom { get => new byte[] { 0xEF, 0xBB, 0xBF }; } /// CRLF。 public const string CRLF = "\r\n"; /// LF。 public const string LF = "\n"; /// 空文本。 public const string Empty = ""; /// 返回表示指定对象的字符串。 public static string Text(object value) { if (value is string str) return str; if (value == null) return null; if (value.Equals(DBNull.Value)) return null; if (value is Type t) return t.Name; if (value is char[] chars) return new string(chars); var type = value.GetType(); var toString = type.GetMethod(nameof(object.ToString), Type.EmptyTypes); if (toString.DeclaringType.Equals(type)) { try { return value.ToString(); } catch { return null; } } return "<" + type.Name + ">"; } /// 字符串为空。 public static bool IsEmpty(string text) => text == null || text == Empty; /// 字符串不为空。 public static bool NotEmpty(string text) => text != null && text != Empty; /// 字符串为空,或只含有空白字符。 public static bool IsBlank(string text) { if (IsEmpty(text)) return true; var length = text.Length; var bcs = BlankChars.ToCharArray(); var bcl = bcs.Length; bool b; char c; for (var i = 0; i < length; i++) { c = text[i]; b = false; for (var j = 0; j < bcl; j++) { if (c == bcs[j]) { b = true; break; } } if (!b) return false; } return true; } /// 字符串不为空,切含有非空白字符。 public static bool NotBlank(string text) => !IsBlank(text); /// 获取文本的 Int32 哈希。 private static int HashCode(string text) { if (text == null) return 0; int hash = 0; var length = text.Length; for (int i = 0; i < length; i++) { hash = 31 * hash + text[i]; } return hash; } private static string PrivateJoin(string separator, IEnumerable cells) { if (cells == null) return Empty; if (cells is string str) return str ?? ""; var sb = new StringBuilder(); var first = true; var hasSeparator = !string.IsNullOrEmpty(separator); foreach (var cell in cells) { if (cell == null) continue; var text = null as string; if (cell is string) text = cell as string; else if (cell is Type type) text = type.Name; else cell.ToString(); if (string.IsNullOrEmpty(text)) continue; if (!first && hasSeparator) sb.Append(separator); first = false; sb.Append(text); } var result = sb.ToString(); return result; } /// 合并为字符串。 public static string Merge(params object[] cells) => Join(null, cells); /// 合并为字符串。 public static string Join(string separator, params object[] cells) { if (cells == null) return Empty; while (cells.Length == 1) { var first = cells[0]; if (first.IsNull()) return Empty; if (first is string str) return str ?? Empty; if (first is IEnumerable chars) { var list = new List(); foreach (var @char in chars) list.Add(@char); return new string(list.ToArray()); } if (!first.GetType().IsValueType && first is IEnumerable enumerable) { var list = new List(); foreach (var item in enumerable) list.Add(item); cells = list.ToArray(); continue; } break; } { var sb = new StringBuilder(); var first = true; var hasSeparator = !string.IsNullOrEmpty(separator); foreach (var cell in cells) { if (cell.IsNull()) continue; var text = null as string; if (cell is string str) text = str; else if (cell is char[] chars) text = new string(chars); else text = Text(cell); if (string.IsNullOrEmpty(text)) continue; if (hasSeparator) { if (first) first = false; else sb.Append(separator); } sb.Append(text); } var result = sb.ToString(); return result; } } /// 重复指定字符,直到达到指定长度。 /// 要重复的字符。 /// 重复的次数。 public static string Duplicate(char cell, int count) { if (count < 1) return Empty; var chars = new char[count]; for (var i = 0; i < count; i++) chars[i] = cell; return new string(chars); } /// 重复指定字符串,直到达到指定长度。 /// 要重复的字符串。 /// 重复的次数。 public static string Duplicate(string cell, int count) { if (IsEmpty(cell) || count < 1) return Empty; var length = cell.Length; var total = length * count; var output = new char[total]; var input = cell.ToCharArray(); for (var i = 0; i < count; i++) { Array.Copy(input, 0, output, length * i, length); } return new string(output); } /// 获取指定长的的空格。 public static string Space(int length) => Duplicate(' ', length); /// 将文本以转换为字节数组。默认 Encoding 为 UTF-8。 public static byte[] Bytes(string text, Encoding encoding = null) { if (text == null || text == Empty) return BytesUtility.Empty; try { return (encoding ?? Encoding.UTF8).GetBytes(text); } catch { return BytesUtility.Empty; } } /// 将字节数组转换为文本。默认 Encoding 为 UTF-8。 public static string FromBytes(byte[] bytes, Encoding encoding = null) { if (bytes == null || bytes.LongLength < 1L) return Empty; try { return (encoding ?? Encoding.UTF8).GetString(bytes); } catch { return Empty; } } /// 将明文文本以 UTF-8 转换为 Base64 文本。 public static string ToBase64(string plain) { if (plain == null || plain == Empty) return Empty; return BytesUtility.ToBase64(Bytes(plain)); } /// 将 Base64 文本以 UTF-8 转换为明文文本。 public static string FromBase64(string cipher) { if (cipher == null || cipher == Empty) return Empty; return FromBytes(BytesUtility.FromBase64(cipher)); } /// 从字符串中删除子字符串。 /// 要查询的字符串。 /// 子字符串。 /// 是否忽略大小写。 /// 删除子字符串后的父字符串。 private static string Exlude(string text, string sub, bool ignoreCase = false) { if (string.IsNullOrEmpty(text)) return ""; if (string.IsNullOrEmpty(sub)) return text; try { string vp = text; string vs = sub; string vr = ""; int vl; int vi; if (ignoreCase) { vp = TextHelper.LCase(vp); vs = TextHelper.LCase(vs); } vl = vs.Length; vi = 1; while (vi <= (vp.Length - vl + 1)) { if (TextHelper.Middle(vp, vi, vl) == vs) { vi = vi + vl; } else { vr = vr + TextHelper.Middle(text, vi, 1); vi = vi + 1; } } return vr; } catch { return text; } } /// 替换字符串中的子字符串。 /// 要查询的字符串。 /// 新子字符串,保留大小写。 /// 原子字符串。 /// 查找时是否忽略父字符串和原子字符串大小写。 /// 替换后的父字符串。 private static string Replace(string text, string old, string @new, bool ignoreCase = false) { if (string.IsNullOrEmpty(text)) return ""; if (string.IsNullOrEmpty(old)) return text; if (string.IsNullOrEmpty(@new)) return Exlude(text, old, ignoreCase); if (TextHelper.Len(text) < TextHelper.Len(old)) return text; if (ignoreCase) { try { string p = TextHelper.LCase(text); string o = TextHelper.LCase(old); int vil = TextHelper.Len(old); int viv = 1; int vend = TextHelper.Len(text) - vil + 1; string vcell; string vresult = ""; while (viv <= vend) { vcell = TextHelper.Middle(p, viv, vil); if (vcell == o) { vresult = vresult + @new; viv = viv + vil; } else { vresult = vresult + TextHelper.Middle(text, viv, 1); viv = viv + 1; } } return vresult; } catch { return text; } } else { try { string vresult = text.Replace(old, @new); return vresult; } catch { return ""; } } } /// 修复文本前缀。 /// 原文本。 /// TRUE:追加指定缀;FALSE:去除指定缀。 /// 前缀文本。 public static string AssureStarts(string text, string head, bool include = true) { if (string.IsNullOrEmpty(text)) return Empty; if (string.IsNullOrEmpty(head)) return text; if (include) { return text.StartsWith(head) ? text : (head + text); } else { var headLength = head.Length; if (!text.StartsWith(head)) return text; var result = text; while (true) { result = result.Substring(headLength); if (!result.StartsWith(head)) return result; } } } /// 修复文本后缀。 /// 原文本。 /// TRUE:追加指定后缀;FALSE:去除指定后缀。 /// 后缀文本。 public static string AssureEnds(string text, string foot, bool include = true) { if (string.IsNullOrEmpty(text)) return Empty; if (string.IsNullOrEmpty(foot)) return text; if (include == true) { return text.EndsWith(foot) ? text : (text + foot); } else { var footLength = foot.Length; if (!text.EndsWith(foot)) return text; var result = text; while (true) { result = result.Substring(0, result.Length - footLength); if (!result.EndsWith(foot)) return result; } } } /// 用单字符作为分隔符拆分文本。 public static string[] Split(string text, char separator) { if (text == null) return new string[0]; if (text.Length < 1) return new string[] { "" }; if ((object)separator == null) return new string[] { text }; return text.Split(separator); } /// 用字符串作为分隔符拆分文本。 public static string[] Split(string text, string separator) { if (text == null) return new string[0]; if (text.Length < 1) return new string[] { "" }; if (string.IsNullOrEmpty(separator)) return new string[] { text }; if (separator.Length > text.Length) return new string[] { text }; var list = new List(); var position = 0; var total = text.Length; var length = separator.Length; var cell = new StringBuilder(); while (position < total) { var read = null as string; if (position + length < total) read = text.Substring(position, length); else read = text.Substring(position); if (read == separator) { if (cell.Length > 0) { list.Add(cell.ToString()); // cell.Clear(); cell = new StringBuilder(); } else { list.Add(""); } position += length; } else { cell.Append((char)text[position]); position += 1; } if (position >= total) { list.Add(cell.ToString()); } } var array = list.ToArray(); return array; } /// 用多个分隔符拆分文本。 public static string[] Split(string text, params char[] separators) { if (text == null) return new string[0]; if (text.Length < 1) return new string[] { "" }; if (separators == null || separators.Length < 1) return new string[] { text }; if (separators.Length == 1) return Split(text, separators[0]); var list = new List(); var separatorsText = new string(separators); var sb = new StringBuilder(); foreach (var c in text) { if (separatorsText.IndexOf(c) >= 0) { list.Add(sb.ToString()); //sb.Clear(); sb = new StringBuilder(); continue; } sb.Append(c); } list.Add(sb.ToString()); #if !NET20 sb.Clear(); #endif return list.ToArray(); } /// 移除字符串前后的空字符。 /// 原始字符串。 /// 移除空格、全角空格、换行符、回车符、制表符和换页符。 public static string Trim(string text, bool trimBlank = false) { if (text == null || text == Empty) return Empty; if (!trimBlank) return Trim(text, ' '); return Trim(text, BlankChars.ToCharArray()); } /// 移除字符串前后的指定字符。 /// 原始字符串。 /// 要移除的字符。 public static string Trim(string text, params char[] chars) { if (text == null || text == Empty) return Empty; if (chars == null || chars.Length < 1) return text; var length = text.Length; var charsLength = chars.Length; var starts = 0; var offset = 0; while (true) { if (offset >= length) break; var c = text[offset]; var trim = false; for (var i = 0; i < charsLength; i++) { if (c == chars[i]) { starts += 1; offset += 1; trim = true; break; } } if (trim) continue; else break; } var ends = 0; if (starts < length) { offset = length - 1; while (true) { if (offset <= starts) break; var c = text[offset]; var trim = false; for (var i = 0; i < charsLength; i++) { if (c == chars[i]) { ends += 1; offset -= 1; trim = true; break; } } if (trim) continue; else break; } } if (starts == 0 && ends == 0) return text; return text.Substring(starts, length - starts - ends); } /// 修剪字符串数组,修剪元素,并去除空字符串。 public static string[] Trim(this IEnumerable strings) { var ab = new ArrayBuilder(); if (strings == null) return ab.Export(); foreach (var str in strings) { var trim = Trim(str); if (string.IsNullOrEmpty(trim)) continue; ab.Add(trim); } var array = ab.Export(); return array; } /// 剪取文本内容,若指定头部为空则从原文本首部起,若指定尾部为空则至原文本末尾。 /// 剪取后的内容,不包含 head 和 foot。 public static string Cut(string text, string head = null, string foot = null) { if (IsEmpty(text)) return Empty; int start, length; if (IsEmpty(head)) { // none if (IsEmpty(foot)) return Empty; // foot length = text.IndexOf(foot); if (length < 1) return text; return text.Substring(0, length); } else { if (IsEmpty(foot)) { // head start = text.IndexOf(head); if (start < 0) return text; start += head.Length; return text.Substring(start); } // both start = text.IndexOf(head); if (start < 0) start = 0; else start += head.Length; var temp = start == 0 ? text : text.Substring(start); length = temp.IndexOf(foot); if (length < 0) return temp; if (length == 0) return Empty; return temp.Substring(0, length); } } /// 比较两个字符串的相似度。返回值大于 0,小于等于 1。 /// /// /// public static double Similarity(string arg1, string arg2) => Levenshtein.Compute(arg1, arg2).Rate; /// 生成新的 GUID。 public static string Guid(bool hyphenation = true, bool lower = true) { var guid = System.Guid.NewGuid(); var str = hyphenation ? guid.ToString() : guid.ToString("n"); if (!lower) str = str.ToUpper(); return str; } /// 生成新主键。 public static string Key() => System.Guid.NewGuid().ToString("n"); /// 生成随机字符串,出现的字符由字符池指定,默认池包含数字和字母。 /// 随机字符串的长度。 /// 字符池,字符池中每个字符在随机字符串中出现的概率约等。 public static string Random(int length, string pool = "0123456789abcdefghijklmnopqrstuvwxyz") { if (length < 1) return Empty; if (IsEmpty(pool)) return Duplicate(SpaceDbc, length); var array = new char[length]; var max = pool.Length - 1; for (var i = 0; i < length; i++) array[i] = pool[NumberUtility.Random(0, max)]; return new string(array); } /// 对字符串列表去重。指定 valid 参数时将去除无效字符串。 public static List Distinct(IEnumerable strings, bool valid = false) { if (strings == null) return new List(); const string space = " "; var count = strings.Count(); var array = new string[count]; var added = 0; var hasNull = false; var hasEmpty = false; var hasSpace = false; foreach (var s in strings) { if (s == null) { if (valid) continue; if (hasNull) continue; hasNull = true; array[added] = s; added += 1; continue; } if (s == Empty) { if (valid) continue; if (hasEmpty) continue; hasEmpty = true; array[added] = s; added += 1; continue; } if (s == space) { if (valid) continue; if (hasSpace) continue; hasSpace = true; array[added] = s; added += 1; continue; } var exist = false; for (var i = 0; i < added; i++) { if (array[i] == s) { exist = true; break; } } if (exist) continue; array[added] = s; added += 1; } if (added < 1) return new List(); var list = new List(added); for (var i = 0; i < added; i++) list.Add(array[i]); return list; } /// 约束字符串中的字符,只包含指定的字符。 public static string Restrict(string text, char[] chars) { if (IsEmpty(text)) return Empty; if (chars == null || chars.Length < 1) return Empty; var total = text.Length; var count = chars.Length; var array = new char[total]; var added = 0; for (var i = 0; i < total; i++) { var c = text[i]; for (var j = 0; j < count; j++) { if (c == chars[j]) { array[added] = c; added += 1; break; } } } if (added < 1) return Empty; return new string(array, 0, added); } /// 约束字符串中的字符,只包含指定的字符。 public static string Restrict(string text, string chars) => IsEmpty(text) || IsEmpty(chars) ? Empty : Restrict(text, chars.ToCharArray()); /// 约束字符串中的字符,只包含字母。 public static string RestrictLetters(string text) => Restrict(text, LetterChars.ToCharArray()); /// 约束字符串中的字符,只包含数字。 public static string RestrictNumeric(string text) => Restrict(text, NumericChars.ToCharArray()); /// 返回此字符串的安全键副本,只保留数据记录主键中可能出现的字符,默认限制长度为 255 字符。 public static string SafeKey(string text, int maxLength = 255) { if (string.IsNullOrEmpty(text)) return Empty; var input = text; var max = maxLength; if (max < 1 || max > input.Length) max = input.Length; // 允许用于主键值的字符。 const string KeyCollection = "-_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; var sb = new StringBuilder(); var total = input.Length; var length = 0; for (var i = 0; i < total; i++) { var c = input[i]; if (KeyCollection.IndexOf(c) < 0) continue; sb.Append(c); length += 1; if (length >= max) break; } var result = sb.ToString(); if (result.Length > max) result = result.Substring(0, max); return result; } /// 追加字符串。 public static StringBuilder Append(StringBuilder builder, params object[] cells) { if (builder != null) builder.Append(Join(null, cells)); return builder; } /// 对 URL 编码。 public static string EncodeUrl(string plain) { return UrlEncoding.Encode(plain); } /// 对 URL 解码。 public static string DecodeUrl(string escaped) { return UrlEncoding.Decode(escaped); } /// 返回此字符串转换为小写形式的副本。 public static string Lower(string text) { if (text == null) return null; else if (text.Length < 1) return Empty; else return text.ToLower(); } /// 返回此字符串转换为大写形式的副本。 public static string Upper(string text) { if (text == null) return null; else if (text.Length < 1) return Empty; else return text.ToUpper(); } /// 检查中国手机号码,包含 13x、14x、15x、16x、17x、18x 和 19x 号段。 public static bool IsPhone(string phone) { if (string.IsNullOrEmpty(phone)) return false; var regex = new Regex(@"^(13|14|15|16|17|18|19)\d{9}$", RegexOptions.None); var match = regex.Match(phone); return match.Success; } /// 渲染 Markdown 文本为 HTML 文本。 public static string RenderMarkdown(string markdown) => MarkdownSharp.Demo.ToHtml(markdown); /// 合并用于启动进程的参数。 public static string MergeProcessArgument(params object[] args) { // var special = " \"\n\r\b\t\f"; var list = new List(); if (args != null) { foreach (var i in args) { var arg = null as string; if (i != null) { if (i is string) arg = i as string; else arg = i.ToString(); } if (string.IsNullOrEmpty(arg)) { list.Add("\"\""); continue; } if (arg.Contains(" ") || arg.Contains("\"")) { list.Add(Merge("\"", arg.Replace("\"", "\\\""), "\"")); continue; } list.Add(arg); } } var result = Join(" ", list.ToArray()); return result; } /// 合并用于启动进程的参数。 private static string MergeProcessArgument_2(params object[] args) { if (args == null) return ""; if (args.Length < 1) return ""; var sb = new StringBuilder(); for (var i = 0; i < args.Length; i++) { if (i > 0) sb.Append(" "); var arg = null as string; if (args[i] != null) { if (args[i] is string) arg = args[i] as string; else arg = args[i].ToString(); } if (arg.IsEmpty()) { sb.Append("\"\""); continue; } // var special = " \"\n\r\b\t\f"; var special = " \""; if (arg.IndexOfAny(special.ToCharArray()) < 0) { sb.Append(arg); } else { sb.Append("\""); if (arg.NotEmpty()) { foreach (var c in arg) { switch (c) { case '"': sb.Append("\\\""); break; // case '\n': // sb.Append("\\n"); // break; // case '\r': // sb.Append("\\r"); // break; // case '\b': // sb.Append("\\b"); // break; // case '\t': // sb.Append("\\t"); // break; default: sb.Append(c); break; } } } sb.Append("\""); } } var result = sb.ToString(); return result; } /// 字符串仅使用英文。可指定空字符串的返回值。 public static bool IsEnglish(string text, bool ifEmpty = true) { if (string.IsNullOrEmpty(text)) return ifEmpty; var trim = text.Trim(); if (string.IsNullOrEmpty(trim)) return ifEmpty; return Regex.IsMatch(trim, "(^[0-9a-zA-Z_ -;:,~!@#%&=<>~\\(\\)\\.\\$\\^\\`\\'\\\"\\&\\{\\}\\[\\]\\|\\*\\+\\?]{0,80}$)"); } /// 转换文本为驼峰形式。 public static string Camel(string text) { if (string.IsNullOrEmpty(text) || !char.IsUpper(text[0])) return text; var chars = text.ToCharArray(); for (int i = 0; i < chars.Length; i++) { if (i == 1 && !char.IsUpper(chars[i])) break; bool hasNext = (i + 1) < chars.Length; if (i > 0 && hasNext) { var nextChar = chars[i + 1]; if (!char.IsUpper(nextChar)) { if (char.IsSeparator(nextChar)) chars[i] = char.ToLowerInvariant(chars[i]); break; } } chars[i] = char.ToLowerInvariant(chars[i]); } return new string(chars); } /// 移除参数 chars 中的每一个字符。 public static string RemoveChars(string text, string chars) { if (IsEmpty(text)) return Empty; if (IsEmpty(chars)) return text; return RemoveChar(text, chars.ToCharArray()); } /// 移除指定的一个或多个字符。 public static string RemoveChar(string text, params char[] chars) { if (IsEmpty(text)) return Empty; if (chars == null || chars.Length < 1) return text; var length = text.Length; var array = new char[length]; var count = chars.Length; var added = 0; for (var i = 0; i < length; i++) { var c = text[i]; var removed = false; for (var j = 0; j < count; j++) { if (c == chars[j]) { removed = true; break; } } if (removed) continue; array[added] = c; added += 1; } if (added < 1) return Empty; return new string(array, 0, added); } /// 防注入,去除常见的功能符号。可限定字符串长度。 public static string AntiInject(string text, int length = -1, string chars = "\"'`\b\f\n\r\t\\/:*?<>|@") { if (IsEmpty(text) || length == 0) return Empty; var t = Trim(text); t = RemoveChars(t, chars); if (length > 0 && t.Length > length) t = t.Substring(0, length); return t; } /// 获取指定长度的字符串片段,可指定 trim 参数对片段再次修剪。 public static string Left(string text, int maxLength, bool trim = false, bool trimBlank = false) { if (IsEmpty(text)) return Empty; if (maxLength > 0 && text.Length > maxLength) { var left = text.Substring(0, maxLength); return trim ? Trim(left, trimBlank) : left; } else return trim ? Trim(text, trimBlank) : text; } /// 获取指定长度的字符串片段,可指定 trim 参数对片段再次修剪。 public static string Right(string text, int maxLength, bool trim = false, bool trimBlank = false) { if (IsEmpty(text)) return Empty; if (maxLength > 0 && text.Length > maxLength) { var left = text.Substring(text.Length - maxLength); return trim ? Trim(left, trimBlank) : left; } else return trim ? Trim(text, trimBlank) : text; } /// 获取字符串片段,起始位置从 0 开始,可指定 trim 参数对片段再次修剪。 /// public static string Middle(string text, int startIndex, int maxLength = -1, bool trim = false, bool trimBlank = false) { if (IsEmpty(text) || maxLength == 0) return Empty; var total = text.Length; var start = startIndex; var length = maxLength; if (start < 0) { length = length + start; start = 0; } if (maxLength < 0) length = total; if (start + length > total) length = total - start; if (start == 0 && length == total) return trim ? Trim(text, trimBlank) : text; var middle = text.Substring(start, length); return trim ? Trim(middle, trimBlank) : middle; } #region Lock static Locker _locker = new Locker(); /// 锁定文本,在锁中执行函数。 /// 要锁定的文本。 /// 要在锁中执行的函数。 /// public static T Lock(this string text, Func inLock) { if (inLock == null) throw new ArgumentNullException(nameof(inLock)); return _locker.InLock(text, inLock); } /// 锁定文本,在锁中执行函数。 /// 要锁定的文本。 /// 要在锁中执行的函数。 /// public static void Lock(this string text, Action inLock) { if (inLock == null) throw new ArgumentNullException(nameof(inLock)); _locker.InLock(text, inLock); } #endregion #region encoding /// 检查字节数组包含 UTF-8 BOM 头。 public static bool ContainsBOM(byte[] bytes) { if (bytes == null) return false; if (bytes.LongLength < 3L) return false; return bytes[0L] == 0xEF && bytes[1L] == 0xBB && bytes[2L] == 0xBF; } /// 检查字节数组是 UTF-8 文本。可指定检查的最大字节长度。 /// 要检查的字节数组。 /// 已检查的偏移量。 /// 检查的最大字节长度。 public static bool IsUTF8(byte[] bytes, Class offset, int checkLength = 1048576) { return IsUTF8(bytes, offset, checkLength); } /// 检查字节数组是 UTF-8 文本,默认最多检测 1MB 数据。 /// 要检查的字节数组。 /// 检查的最大字节长度,指定为 0 将不限制检查长度。 /// 已检查的偏移量,用于调试。 public static bool IsUTF8(byte[] bytes, int checkLength = 1048576, Class offset = null) { // UTF8在Unicode的基础上制定了这样一套规则: // 1.对于单字节字符,比特位的最高位为0; // 2.对于多字节字符,第一个字节的比特位中,最高位有n个1,剩下的n - 1个字节的比特位中,最高位都是10。 // 好了,我知道你一定看不懂,那就先来看看下面例子后,再去看上面定义吧。 // 比如一个字符(“A”),它在UTF8中的编码为(用二进制表示):01000001。由于比特位的最高位是0,表示它是单字节,它只需要1个字节就可以表示。 // 再比如一个字符(“判”),它在UTF8中的编码为(用二进制表示):11100101 10001000 10100100。由于在第一个字节中,比特位最高位有3个1,说明这个字符总共需要3个字节来表示,且后3 - 1 = 2位字节中,比特位的最高位为10。 if (bytes == null) return false; var length = bytes.LongLength; // 检查 BOM 头。 if (ContainsBOM(bytes)) return true; var hasOffset = offset != null; var append = 0; if (hasOffset) offset.Value = 0; for (int i = 0; i < length; i++) { if (checkLength > 0 && i >= checkLength) break; var b = bytes[i]; if (hasOffset) offset.Value = i; // 追加字节最高位为 0。 if (append > 0) { if (b >> 6 != 2) return false; append -= 1; continue; } // ASCII 字符。 if (b < 128) continue; // 2 字节 UTF-8。 if (b >= 0xC0 && b <= 0xDF) { append = 1; continue; } // 3 字节 UTF-8 字符。 if (b >= 0xE0 && b <= 0xEF) { append = 2; continue; } // 4 字节 UTF-8 字符。 if (b >= 0xF0 && b <= 0xF7) { append = 3; continue; } // 5 字节 UTF-8 字符。 if (b >= 0xF8 && b <= 0xFB) { append = 4; continue; } // 6 字节 UTF-8 字符。 if (b >= 0xFC && b <= 0xFD) { append = 5; continue; } // 未知字节,非 UTF-8 定义。 return false; } return true; } /// 解析编码名称。 /// 解析失败时,返回 NULL 值。 private static Encoding ParseEncoding(string encoding) { if (encoding.IsEmpty()) return null; var lower = encoding.Lower(); var nick = lower.Replace("-", ""); switch (nick) { case "ascii": return Encoding.ASCII; case "bigendia": case "bigendianunicode": return Encoding.BigEndianUnicode; case "utf7": return Encoding.UTF7; case "utf8": return Encoding.UTF8; case "utf16": case "unicode": return Encoding.Unicode; case "utf32": return Encoding.UTF7; case "default": return Encoding.Default; case "ansi": case "gb2312": case "gb18030": return Encoding.Default; } return null; } #endregion } }