StrUtil_StrChunk.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. using System;
  2. using System.Collections.Generic;
  3. namespace ET.PackageManager.Editor
  4. {
  5. [Serializable]
  6. public struct StrChunk
  7. {
  8. /// <summary>
  9. /// 字符串,或者key
  10. /// 如果是字符串,keyIndex = 0;
  11. /// 如果是key,keyIndex表示索引,从1开始
  12. /// </summary>
  13. public string TextOrKey;
  14. /// <summary>
  15. /// 如果StrOrKey为Key时的索引值,从1开始
  16. /// </summary>
  17. public byte KeyIndex;
  18. }
  19. public static partial class StrUtil
  20. {
  21. /// <summary>
  22. /// 高性能处理字符串中{%key%num}形式的占位符
  23. /// 其中key为连续的小写字母,nul为非0开头的连续数字
  24. /// 返回一个StrChunk数组,方便使用的地方缓存以加速替换占位符
  25. /// </summary>
  26. /// <param name="value"></param>
  27. /// <param name="throwError">
  28. /// 是否抛出错误
  29. /// 如果为false,则会进行容错性解析
  30. /// </param>
  31. /// <returns></returns>
  32. public static StrChunk[] GetStrChunk(string value, bool throwError = false)
  33. {
  34. if (string.IsNullOrEmpty(value))
  35. {
  36. return Array.Empty<StrChunk>();
  37. }
  38. var chunks = g_cacheStrChunkList;
  39. var sb = SbPool.Get();
  40. var keySb = SbPool.Get();
  41. var indexSb = SbPool.Get();
  42. int throwErrId = -1;
  43. char lastChar = char.MaxValue;
  44. ReadChunkState state = ReadChunkState.ReadText;
  45. const char beginChar = '{';
  46. const char endChar = '}';
  47. int chrCount = value.Length;
  48. for (int i = 0; i < chrCount; i++)
  49. {
  50. char c = value[i];
  51. switch (state)
  52. {
  53. case ReadChunkState.ReadText:
  54. switch (c)
  55. {
  56. case beginChar:
  57. state = ReadChunkState.ReadyReadKey;
  58. break;
  59. case endChar:
  60. state = ReadChunkState.Escape;
  61. break;
  62. default:
  63. sb.Append(c);
  64. break;
  65. }
  66. break;
  67. case ReadChunkState.ReadyReadKey:
  68. if (c == beginChar)
  69. {
  70. //{{,转义为{
  71. sb.Append(beginChar);
  72. state = ReadChunkState.ReadText;
  73. break;
  74. }
  75. if (c < 'a' || c > 'z')
  76. {
  77. throwErrId = 1;
  78. sb.Append(beginChar).Append(c);
  79. state = ReadChunkState.ReadText;
  80. break;
  81. }
  82. keySb.Append(c);
  83. state = ReadChunkState.ReadKey;
  84. break;
  85. case ReadChunkState.ReadKey:
  86. if (c >= 'a' && c <= 'z')
  87. {
  88. keySb.Append(c);
  89. break;
  90. }
  91. if (c >= '1' && c <= '9')
  92. {
  93. indexSb.Append(c);
  94. state = ReadChunkState.ReadIndex;
  95. break;
  96. }
  97. throwErrId = 2;
  98. sb.Append(beginChar).Append(keySb.ToString()).Append(c);
  99. keySb.Clear();
  100. state = ReadChunkState.ReadText;
  101. break;
  102. case ReadChunkState.ReadIndex:
  103. if (c >= '0' && c <= '9')
  104. {
  105. indexSb.Append(c);
  106. break;
  107. }
  108. if (c == endChar)
  109. {
  110. chunks.Add(new StrChunk() { TextOrKey = sb.ToString() });
  111. chunks.Add(new StrChunk()
  112. {
  113. KeyIndex = byte.Parse(indexSb.ToString()),
  114. TextOrKey = keySb.ToString()
  115. });
  116. sb.Clear();
  117. }
  118. else
  119. {
  120. throwErrId = 3;
  121. sb.Append(beginChar)
  122. .Append(keySb.ToString())
  123. .Append(indexSb.ToString())
  124. .Append(c);
  125. }
  126. keySb.Clear();
  127. indexSb.Clear();
  128. state = ReadChunkState.ReadText;
  129. break;
  130. case ReadChunkState.Escape:
  131. if (c == lastChar)
  132. {
  133. sb.Append(c);
  134. }
  135. else
  136. {
  137. throwErrId = 0;
  138. sb.Append(lastChar).Append(c);
  139. }
  140. state = ReadChunkState.ReadText;
  141. break;
  142. }
  143. //处理错误
  144. if (throwError && throwErrId > -1)
  145. {
  146. switch (throwErrId)
  147. {
  148. case 0:
  149. throw new Exception(string.Format("text={3}, index={0}, chr='{1}':此处只能出现'{2}'", i, c,
  150. lastChar, value));
  151. case 1:
  152. throw new Exception(string.Format("text={2}, index={0}, chr='{1}', 此处只能出现小写英文字符", i, c,
  153. value));
  154. case 2:
  155. throw new Exception(string.Format("text={2}, index={0}, chr='{1}', 此处只能出现小写英文或1-9的数字", i, c,
  156. value));
  157. case 3:
  158. throw new Exception(string.Format("text={2}, index={0}, chr='{1}', 0-9的数字或'}}'", i, c,
  159. value));
  160. }
  161. }
  162. lastChar = c;
  163. }
  164. //结尾处理
  165. if (state != ReadChunkState.ReadText)
  166. {
  167. if (throwError)
  168. {
  169. throw new Exception("没有正确结束, state=" + state.ToString());
  170. }
  171. sb.Append(keySb.ToString()).Append(indexSb.ToString());
  172. }
  173. if (sb.Length > 0)
  174. {
  175. chunks.Add(new StrChunk() { TextOrKey = sb.ToString() });
  176. }
  177. SbPool.Put(sb);
  178. SbPool.Put(keySb);
  179. SbPool.Put(indexSb);
  180. var arr = chunks.ToArray();
  181. chunks.Clear();
  182. return arr;
  183. }
  184. private static readonly List<StrChunk> g_cacheStrChunkList = new List<StrChunk>();
  185. private enum ReadChunkState
  186. {
  187. ReadText,
  188. ReadyReadKey,
  189. ReadKey,
  190. ReadIndex,
  191. //转义处理
  192. Escape
  193. }
  194. }
  195. }