如何实现不重启枚举的重复LINQ查询解析重复模式字符串?
用LINQ实现重复模式字符串的分段解析
要实现你想要的Repeat方法,核心是维护一个共享的枚举器,让每次LINQ操作都从上一次处理结束的位置继续推进,而不是从头遍历整个字符串。以下是完整的实现代码:
1. 实现Repeat扩展方法
创建静态扩展类,实现Repeat方法:
using System; using System.Collections.Generic; using System.Linq; public static class EnumerableExtensions { // 重复对同一个枚举器应用selector,直到枚举耗尽,每次返回selector的处理结果 public static IEnumerable<TResult> Repeat<T, TResult>(this IEnumerable<T> source, Func<IEnumerable<T>, TResult> selector) { using var enumerator = source.GetEnumerator(); while (true) { // 检查是否还有剩余元素可处理 if (!enumerator.MoveNext()) yield break; // 创建一个从当前枚举位置开始的可枚举序列,消费时会推进共享枚举器 IEnumerable<T> remaining = EnumerateFromCurrent(enumerator); // 应用传入的LINQ逻辑,获取当前段的结果 TResult result = selector(remaining); yield return result; } } // 辅助方法:从枚举器当前位置开始枚举,直到结束 private static IEnumerable<T> EnumerateFromCurrent<T>(IEnumerator<T> enumerator) { yield return enumerator.Current; while (enumerator.MoveNext()) { yield return enumerator.Current; } } }
2. 实现你的GetContentOfEachP方法
现在可以直接用你设想的LINQ链式调用:
using Newtonsoft.Json.Linq; public JToken[] GetContentOfEachP(string text) { return text .Repeat(enumerable => enumerable .SkipWhile(c => c != ':') // 跳过键名部分(直到遇到冒号) .Skip(1) // 跳过冒号本身 .TakeWhile(c => c != '\r') // 提取冒号到\r之间的内容 .ToArray() ) .Select(charArray => JToken.Parse(new string(charArray))) .ToArray(); }
3. 测试代码
调用方式和你预期的一致:
var input = "p0:{foo:bar}\r\np1:1234\r\np2:abcd"; foreach (var p in GetContentOfEachP(input)) { Console.WriteLine(p.ToString()); }
输出结果:
{ "foo": "bar" } 1234 "abcd"
关键逻辑说明
- 共享枚举器:Repeat方法内部只创建一个枚举器,所有分段处理都复用这个枚举器,确保每次处理都从上一次结束的位置继续。
- EnumerateFromCurrent辅助方法:把当前枚举器包装成可枚举序列,让SkipWhile/TakeWhile等LINQ方法可以直接消费,同时自动推进枚举器的位置。
- 分段推进:每次调用selector后,枚举器已经停在当前段的末尾(比如
\r的位置),下一次循环会跳过这个位置,继续处理下一段。
关于生产环境的说明
正如你提到的,生产环境中用text.Split("\r\n")再逐个处理的方式更简洁易读,维护成本更低。这个LINQ实现更多是满足技术探索的好奇心,展示如何通过控制枚举器来实现分段遍历。
内容的提问来源于stack exchange,提问作者Thick_propheT
相关产品推荐
相关产品推荐

