经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
高性能版本的零内存分配LikeString函数(ZeroMemAllocLikeOperator)
来源:cnblogs  作者:VAllen  时间:2024/6/13 10:50:33  对本文有异议

继上一篇文章在.NET Core,除了VB的LikeString,还有其它方法吗?(四种LikeString实现分享)分享了四种实现方式,笔者对这四种实现方式,不管是执行性能还是内存分配性能上,都不太满意。

那么是否有好的实现方法呢?答案是有的。

今天我们就搬出ReadOnlySpan<T>这个非常好用的结构类型,它是在 .NET Core 2.1 中新引入的类型,与它一同被引入的类型还有:

  • System.Span: 这以类型安全和内存安全的方式表示任意内存的连续部分;
  • System.ReadOnlySpan: 这表示任意连续内存区域的类型安全和内存安全只读表示形式;
  • System.Memory: 这表示一个连续的内存区域;
  • System.ReadOnlyMemory: 类似ReadOnlySpan, 此类型表示内存的连续部分ReadOnlySpan, 它不是 ByRef 类型;

    注:ByRef 类型指的是 ref readonly struct

下面,我们就来看看如何实现高性能和零内存分配的 LikeString 函数吧!

  1. #nullable enable
  2. using System;
  3. namespace AllenCai
  4. {
  5. /// <summary>
  6. /// 这是一个模仿Microsoft.VisualBasic.CompilerServices.LikeOperator.LikeString方法,<br />
  7. /// 实现支持*和?通配符和支持忽略大小写规则以及区域无关性的匹配。<br />
  8. /// 该实现的目的是为了减少内存分配,提高性能。
  9. /// </summary>
  10. public class ZeroMemAllocLikeOperator
  11. {
  12. /// <summary>
  13. /// 对给定的两个字符串执行比较,支持使用*和?通配符。
  14. /// </summary>
  15. public static bool LikeString(string? content, string? pattern, bool ignoreCase = true, bool useInvariantCulture = true)
  16. {
  17. if (content == null && pattern == null)
  18. return true;
  19. if (content == null || pattern == null)
  20. return false;
  21. ReadOnlySpan<char> patternSpan = pattern.AsSpan();
  22. ReadOnlySpan<char> contentSpan = content.AsSpan();
  23. return LikeString(contentSpan, patternSpan, ignoreCase, useInvariantCulture);
  24. }
  25. /// <summary>
  26. /// 对给定的两个字符Span执行比较,支持使用*和?通配符。
  27. /// </summary>
  28. public static bool LikeString(ReadOnlySpan<char> contentSpan, ReadOnlySpan<char> patternSpan, bool ignoreCase = true, bool useInvariantCulture = true)
  29. {
  30. char zeroOrMoreChars = '*';
  31. char oneChar = '?';
  32. // 如果pattern是由1个星号*组成,那么没必要匹配,直接返回true。
  33. if (patternSpan.Length == 1)
  34. {
  35. ref readonly char patternItem = ref patternSpan[0];
  36. if (patternItem == zeroOrMoreChars)
  37. {
  38. return true;
  39. }
  40. }
  41. // 如果被匹配内容的长度只有1位,而pattern刚好也是一个问号?,那么没必要匹配,直接返回true。
  42. if (contentSpan.Length == 1)
  43. {
  44. ref readonly char patternItem = ref patternSpan[0];
  45. if (patternItem == oneChar)
  46. {
  47. return true;
  48. }
  49. }
  50. // 如果pattern是由多个星号*和问号?组成,那么没必要匹配,直接返回true。
  51. int zeroOrMorePatternCount = 0;
  52. int onePatternCount = 0;
  53. for (int i = 0; i < patternSpan.Length; i++)
  54. {
  55. ref readonly char patternItem = ref patternSpan[i];
  56. if (patternItem == zeroOrMoreChars)
  57. {
  58. zeroOrMorePatternCount++;
  59. }
  60. else if (patternItem == oneChar)
  61. {
  62. onePatternCount++;
  63. }
  64. }
  65. if (zeroOrMorePatternCount + onePatternCount == patternSpan.Length)
  66. {
  67. //只要出现1个或多个星号*,那么就没必要在乎被匹配内容的长度了。
  68. if (zeroOrMorePatternCount > 0)
  69. {
  70. return true;
  71. }
  72. //如果没有星号*,全是问号?,那么就检查是否由问号?组成的pattern长度是否和被匹配内容的长度一致。如果一致,没必要匹配,直接返回true。
  73. if (patternSpan.Length == contentSpan.Length)
  74. {
  75. return true;
  76. }
  77. }
  78. // 选择合适的EqualsChar方法。
  79. EqualsCharDelegate equalsChar;
  80. if (ignoreCase)
  81. {
  82. if (useInvariantCulture)
  83. {
  84. equalsChar = EqualsCharInvariantCultureIgnoreCase;
  85. }
  86. else
  87. {
  88. equalsChar = EqualsCharCurrentCultureIgnoreCase;
  89. }
  90. }
  91. else
  92. {
  93. equalsChar = EqualsChar;
  94. }
  95. return LikeStringCore(contentSpan, patternSpan, in zeroOrMoreChars, in oneChar, equalsChar);
  96. }
  97. private static bool LikeStringCore(ReadOnlySpan<char> contentSpan, ReadOnlySpan<char> patternSpan, in char zeroOrMoreChars, in char oneChar, EqualsCharDelegate equalsChar)
  98. {
  99. // 遍历pattern,逐个字符匹配。
  100. int contentIndex = 0;
  101. int patternIndex = 0;
  102. while (contentIndex < contentSpan.Length && patternIndex < patternSpan.Length)
  103. {
  104. ref readonly char patternItem = ref patternSpan[patternIndex];
  105. if (patternItem == zeroOrMoreChars)
  106. {
  107. // 如果pattern中的下一个字符是星号*,那么就一直往后移动patternIndex,直到找到不是星号*的字符。
  108. while (true)
  109. {
  110. if (patternIndex < patternSpan.Length)
  111. {
  112. ref readonly char nextPatternItem = ref patternSpan[patternIndex];
  113. if (nextPatternItem == zeroOrMoreChars)
  114. {
  115. patternIndex++;
  116. continue;
  117. }
  118. }
  119. break;
  120. }
  121. // 如果patternIndex已经到了pattern的末尾,那么就没必要再匹配了,直接返回true。
  122. if (patternIndex == patternSpan.Length)
  123. {
  124. return true;
  125. }
  126. // 如果patternIndex还没到pattern的末尾,那么就从contentIndex开始匹配。
  127. while (contentIndex < contentSpan.Length)
  128. {
  129. if (LikeStringCore(contentSpan.Slice(contentIndex), patternSpan.Slice(patternIndex), in zeroOrMoreChars, in oneChar, equalsChar))
  130. {
  131. return true;
  132. }
  133. contentIndex++;
  134. }
  135. return false;
  136. }
  137. if (patternItem == oneChar)
  138. {
  139. // 如果pattern中的下一个字符是问号?,那么就匹配一个字符。
  140. contentIndex++;
  141. patternIndex++;
  142. }
  143. else
  144. {
  145. // 如果pattern中的下一个字符不是星号*,也不是问号?,那么就匹配一个字符。
  146. if (contentIndex >= contentSpan.Length)
  147. {
  148. return false;
  149. }
  150. ref readonly char contentItem = ref contentSpan[contentIndex];
  151. if (!equalsChar(in contentItem, in patternItem))
  152. {
  153. return false;
  154. }
  155. //if (ignoreCase)
  156. //{
  157. // if (char.ToUpperInvariant(contentItem) != char.ToUpperInvariant(patternItem))
  158. // {
  159. // return false;
  160. // }
  161. //}
  162. //else
  163. //{
  164. // if (contentItem != patternItem)
  165. // {
  166. // return false;
  167. // }
  168. //}
  169. contentIndex++;
  170. patternIndex++;
  171. }
  172. }
  173. // 如果content都匹配完了,而pattern还没遍历完,则检查剩余的patternItem是否都是星号*,如果是就返回true,否则返回false。
  174. if (contentIndex == contentSpan.Length)
  175. {
  176. // 如果pattern中的下一个字符是星号*,那么就一直往后移动patternIndex,直到找到不是星号*的字符。
  177. while (true)
  178. {
  179. if (patternIndex < patternSpan.Length)
  180. {
  181. ref readonly char nextPatternItem = ref patternSpan[patternIndex];
  182. if (nextPatternItem == zeroOrMoreChars)
  183. {
  184. patternIndex++;
  185. continue;
  186. }
  187. }
  188. break;
  189. }
  190. return patternIndex == patternSpan.Length;
  191. }
  192. return false;
  193. }
  194. private static bool EqualsChar(in char contentItem, in char patternItem)
  195. {
  196. return contentItem == patternItem;
  197. }
  198. private static bool EqualsCharCurrentCultureIgnoreCase(in char contentItem, in char patternItem)
  199. {
  200. return char.ToUpper(contentItem) == char.ToUpper(patternItem);
  201. }
  202. private static bool EqualsCharInvariantCultureIgnoreCase(in char contentItem, in char patternItem)
  203. {
  204. return char.ToUpperInvariant(contentItem) == char.ToUpperInvariant(patternItem);
  205. }
  206. private delegate bool EqualsCharDelegate(in char contentItem, in char patternItem);
  207. }
  208. }

PS: 以上代码在 .NET Standard 2.1 项目使用,可直接编译通过。

在 .NET Standard 2.0 项目中,需要额外引入 System.Memory 这个 NuGet 包,且需要将 LangVersion(C#语言版本)更改为 8.0 或更高(通常使用defaultlatest也可以)。

原文链接:https://www.cnblogs.com/VAllen/p/18245425/High-performance-and-zero-memory-allocation-LikeString-function-implementation

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号