如何在C#中用正则匹配删除/替换文本文件中的多行文本
解决C#中移除文本文件特定多行模式内容的问题
针对你的需求,我提供两种可行的解决方案,分别适配不同场景:
方案1:读取整个文件内容,使用正则替换(推荐,代码简洁)
如果你的文件大小适中,直接读取整个文件内容进行正则替换是最便捷的方式,既能保证原格式不被破坏,又能灵活调整匹配规则。
步骤说明:
- 读取原文件的完整文本内容
- 编写精准的正则表达式,匹配需要移除的完整块(包含Example标题行及后续特定结构)
- 执行替换操作,移除所有匹配的目标内容
- 将清理后的内容写入输出文件
完整代码:
using System.IO; using System.Text.RegularExpressions; class Program { static void Main(string[] args) { string inputFileName = "inputFile.txt"; string outputFileName = "outputResult.txt"; // 读取原文件内容 string content = File.ReadAllText(inputFileName, Encoding.Default); // 正则表达式:匹配需要移除的完整块 // 解释:匹配以Example开头且包含"Should be Removed"的行,后续跟着指定的多行空行分隔结构 string pattern = @"(?m)^Example.* - Should be Removed.*\r?\nEvent Number \(#\)\r?\n^\s*$\r?\n.*\r?\n^\s*$\r?\n.*\r?\n^\s*$\r?\n.*\r?\n^\s*$\r?\n?"; // 执行替换,忽略大小写(避免因大小写差异漏匹配) string cleanedContent = Regex.Replace(content, pattern, string.Empty, RegexOptions.IgnoreCase); // 写入清理后的内容到输出文件 File.WriteAllText(outputFileName, cleanedContent, Encoding.UTF8); } }
正则说明:
(?m):开启多行模式,让^和$匹配每行的开头和结尾^Example.* - Should be Removed.*:匹配需要移除的Example标题行Event Number \(#\):匹配事件编号行(括号在正则中是特殊字符,需要转义)^\s*$:匹配空行(包括空格、制表符组成的空白行)\r?\n:兼容Windows和Linux系统的换行符
方案2:逐行处理(适合超大文件,低内存占用)
如果你的文件体积过大,无法一次性加载到内存,可以采用逐行跟踪的方式,识别并跳过需要移除的块。
步骤说明:
- 使用
StreamReader逐行读取原文件 - 维护一个匹配状态机,跟踪当前是否处于需要移除的块中
- 当完整匹配到目标块时,跳过这些行;否则将内容写入输出文件
完整代码:
using System.Collections.Generic; using System.IO; class Program { static void Main(string[] args) { string inputFileName = "inputFile.txt"; string outputFileName = "outputResult.txt"; using (var reader = new StreamReader(inputFileName, Encoding.Default)) using (var writer = new StreamWriter(outputFileName, false, Encoding.UTF8)) { string line; int matchStage = 0; // 跟踪匹配进度:0=未开始,1=匹配Example行,2=匹配Event Number行... List<string> buffer = new List<string>(); while ((line = reader.ReadLine()) != null) { switch (matchStage) { case 0: // 检查是否是要移除的Example标题行 if (line.StartsWith("Example") && line.Contains("Should be Removed")) { buffer.Add(line); matchStage = 1; } else { writer.WriteLine(line); } break; case 1: // 检查是否是Event Number行 if (line.Trim() == "Event Number (#)") { buffer.Add(line); matchStage = 2; } else { // 不匹配,写入缓存内容和当前行 WriteBuffer(buffer, writer); writer.WriteLine(line); ResetState(ref buffer, ref matchStage); } break; case 2: // 检查是否是空行 if (string.IsNullOrWhiteSpace(line)) { buffer.Add(line); matchStage = 3; } else { WriteBuffer(buffer, writer); writer.WriteLine(line); ResetState(ref buffer, ref matchStage); } break; case 3: // 匹配任意非空行(对应string2) if (!string.IsNullOrWhiteSpace(line)) { buffer.Add(line); matchStage = 4; } else { WriteBuffer(buffer, writer); writer.WriteLine(line); ResetState(ref buffer, ref matchStage); } break; case 4: // 检查空行 if (string.IsNullOrWhiteSpace(line)) { buffer.Add(line); matchStage = 5; } else { WriteBuffer(buffer, writer); writer.WriteLine(line); ResetState(ref buffer, ref matchStage); } break; case 5: // 匹配任意非空行(对应string3) if (!string.IsNullOrWhiteSpace(line)) { buffer.Add(line); matchStage = 6; } else { WriteBuffer(buffer, writer); writer.WriteLine(line); ResetState(ref buffer, ref matchStage); } break; case 6: // 检查空行 if (string.IsNullOrWhiteSpace(line)) { buffer.Add(line); matchStage = 7; } else { WriteBuffer(buffer, writer); writer.WriteLine(line); ResetState(ref buffer, ref matchStage); } break; case 7: // 匹配任意非空行(对应string4) if (!string.IsNullOrWhiteSpace(line)) { buffer.Add(line); matchStage = 8; } else { WriteBuffer(buffer, writer); writer.WriteLine(line); ResetState(ref buffer, ref matchStage); } break; case 8: // 检查最后一个空行,完成匹配则跳过 if (string.IsNullOrWhiteSpace(line)) { ResetState(ref buffer, ref matchStage); } else { WriteBuffer(buffer, writer); writer.WriteLine(line); ResetState(ref buffer, ref matchStage); } break; } } // 处理文件末尾剩余的缓存内容 WriteBuffer(buffer, writer); } } // 辅助方法:写入缓存内容 private static void WriteBuffer(List<string> buffer, StreamWriter writer) { foreach (var bufLine in buffer) { writer.WriteLine(bufLine); } } // 辅助方法:重置匹配状态 private static void ResetState(ref List<string> buffer, ref int matchStage) { buffer.Clear(); matchStage = 0; } }
注意事项:
- 若需调整匹配规则,方案1仅需修改正则表达式即可,灵活性更高;方案2则需要调整状态机的判断逻辑。
- 注意编码一致性:原文件用
Encoding.Default读取,输出用Encoding.UTF8写入,确保编码转换正确。 - 测试前请备份原文件,避免数据丢失。
内容的提问来源于stack exchange,提问作者Serega VRS
相关产品推荐
相关产品推荐

