提升列表字符串匹配速度:处理含通配符*的大文本文件
处理含通配符的超大文本文件:删除匹配条目
看起来你已经开始着手解决这个批量数据清理的问题了——处理超过20MB的文本文件,还要根据带*的通配符规则删除匹配条目,这类需求在数据规整场景里挺常见的。我来帮你完善思路和代码,解决内存效率、并行安全和匹配性能的问题。
核心思路拆解
首先得理清两个关键环节:
- 预处理通配符规则:把所有带
*的行转换成匹配前缀(比如700670*提取成700670),去重后得到一个待匹配的前缀集合 - 高效处理大文件:不能把整个文件加载到内存(虽然20MB不算极端,但流式处理更稳妥),逐行读取并判断是否匹配前缀集合,保留不匹配的行
完整代码实现
下面是优化后的代码,我会逐个解释关键部分:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; class Program { static void Main(string[] args) { // 假设rawRules是你从文件读取的原始条目列表 List<string> rawRules = new List<string> { "700670*", "12345*", "888*" }; // 1. 预处理通配符规则,提取前缀并去重 var concurrentPrefixes = new System.Collections.Concurrent.ConcurrentHashSet<string>(); // 并行处理规则(CPU密集型操作,并行能提速) Parallel.ForEach(rawRules, rule => { int starIndex = rule.IndexOf('*'); if (starIndex != -1) { string prefix = rule.Substring(0, starIndex); if (!string.IsNullOrEmpty(prefix)) { concurrentPrefixes.Add(prefix); } } }); // 转成普通HashSet,后续匹配更高效 var prefixesToDelete = new HashSet<string>(concurrentPrefixes); // 2. 流式处理大文件:逐行读取并过滤 string inputFilePath = "input.txt"; string outputFilePath = "output_cleaned.txt"; using (var reader = new StreamReader(inputFilePath)) using (var writer = new StreamWriter(outputFilePath)) { string line; while ((line = reader.ReadLine()) != null) { // 判断当前行是否匹配任何待删除前缀 bool shouldDelete = prefixesToDelete.Any(prefix => line.StartsWith(prefix, StringComparison.Ordinal)); if (!shouldDelete) { writer.WriteLine(line); } } } Console.WriteLine("文件处理完成!"); } }
关键优化点说明
- 线程安全的前缀收集:用
ConcurrentHashSet处理并行添加操作,避免多线程同时操作普通HashSet导致的异常或数据丢失 - 流式文件处理:
StreamReader逐行读取文件,不会把整个20MB文件塞进内存,即使后续文件扩容到几百MB也能轻松应对 - 匹配效率提升:HashSet的
Any操作比普通List更快,如果你有大量前缀,还可以按前缀长度降序排序,优先匹配长前缀(避免短前缀误匹配长前缀的情况,比如888*和888123*,优先检查长前缀能减少误判)
进阶优化:前缀排序+精准匹配
如果你的前缀列表特别长(比如上万个),可以进一步优化匹配逻辑:
// 预处理时按前缀长度降序排序 var sortedPrefixes = concurrentPrefixes.OrderByDescending(p => p.Length).ToList(); // 匹配时优先检查长前缀,避免短前缀提前匹配 bool shouldDelete = sortedPrefixes.Any(prefix => line.Length >= prefix.Length && line.Substring(0, prefix.Length) == prefix);
这样可以避免类似888*先匹配到888123456,而忽略了888123*的精确规则,让匹配逻辑更严谨。
内容的提问来源于stack exchange,提问作者Lukasz Stanulewicz
相关产品推荐
相关产品推荐

