C#中如何用正则提取含换行的完整日志条目(支持分组)
问题:提取含换行的日志完整条目(C#正则)
我是正则表达式新手,已经明确需求,但卡在提取日志中包含换行符的内容上。
测试日志内容
2022-09-21 05:45:24.8603 Debug Sensor SetState Started 2022-09-21 05:45:26.7529 Info Updater SensorDeploymentLogs no value returned from SensorDeployment 2022-09-21 12:37:47.1286 Error TaskAwaiter RunPeriodic <RegisterPeriodicTask>b__1 failed System.Threading.Tasks.TaskCanceledException: A task was canceled. at async Task<HttpResponseMessage> System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task<HttpResponseMessage> sendTask, HttpRequestMessage request, CancellationTokenSource cts, bool disposeCts) at async Task<TResponse> CommunicationWebClient.SendAsync<TResponse>(byte[] requestBytes, int offset, int count) at async Task<TResponse> CommunicationWebClient.SendWithRetryAsync<TResponse>(byte[] requestBytes, int offset, int count) at async Task<TResponse> CommunicationWebClient.SendAsync<TResponse>(IRequestWithResponse<TResponse> request) 2022-09-21 12:42:53.2810 Info Updater Sensor no value returned from SensorDeployment
当前使用的正则
^\d{4}\-(0[1-9]|1[012])\-(0[1-9]|[12][0-9]|3[01]) (?:(?:([01]?\d|2[0-3]):)?([0-5]?\d):)?([0-5]?\d).\d{4}.*
需求说明
希望提取每个以日期/时间开头的完整日志条目(包含日期及后续所有内容,包括换行),目前只能匹配单行日志,无法捕获日期之间带换行的内容。同时希望能把换行的内容单独分组,避免合并成一行,方便阅读。预期提取结果如下:
Result 1: 2022-09-21 05:45:24.8603 Debug Sensor SetState Started Result 2: 2022-09-21 05:45:26.7529 Info Updater SensorDeploymentLogs no value returned from SensorDeployment Result 3: 2022-09-21 12:37:47.1286 Error TaskAwaiter RunPeriodic <RegisterPeriodicTask>b__1 failed System.Threading.Tasks.TaskCanceledException: A task was canceled. at async Task<HttpResponseMessage> System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task<HttpResponseMessage> sendTask, HttpRequestMessage request, CancellationTokenSource cts, bool disposeCts) at async Task<TResponse> CommunicationWebClient.SendAsync<TResponse>(byte[] requestBytes, int offset, int count) at async Task<TResponse> CommunicationWebClient.SendWithRetryAsync<TResponse>(byte[] requestBytes, int offset, int count) at async Task<TResponse> CommunicationWebClient.SendAsync<TResponse>(IRequestWithResponse<TResponse> request) Result 4: 2022-09-21 12:42:53.2810 Info Updater Sensor no value returned from SensorDeployment
注:使用语言为C#。
解决方案
核心思路
日志条目的边界是以日期时间开头的行,因此需要匹配从一个日期时间行开始,到下一个日期时间行之前(或日志结尾)的所有内容。关键是让正则支持跨行匹配,并准确识别条目边界。
带分组的正则表达式
^(\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]) (?:[01]?\d|2[0-3]):[0-5]?\d:[0-5]?\d\.\d{4} .*?)(\n(?!\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]) ).*)*$
正则解析
- 第一分组(主行内容):
(\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]) (?:[01]?\d|2[0-3]):[0-5]?\d:[0-5]?\d\.\d{4} .*?)- 匹配开头的日期时间行,用
.*?非贪婪匹配到换行前的内容,避免过度覆盖后续条目。
- 匹配开头的日期时间行,用
- 第二分组(换行附加内容):
(\n(?!\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]) ).*)*(?!...)负前瞻:排除下一行是日期时间开头的情况;(\n.*)*:匹配所有后续非日期开头的行,统一归入该分组(若需单独捕获每一行,可在匹配后拆分该分组内容)。
C# 代码示例
using System; using System.Text.RegularExpressions; class Program { static void Main() { string logContent = @"2022-09-21 05:45:24.8603 Debug Sensor SetState Started 2022-09-21 05:45:26.7529 Info Updater SensorDeploymentLogs no value returned from SensorDeployment 2022-09-21 12:37:47.1286 Error TaskAwaiter RunPeriodic <RegisterPeriodicTask>b__1 failed System.Threading.Tasks.TaskCanceledException: A task was canceled. at async Task<HttpResponseMessage> System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task<HttpResponseMessage> sendTask, HttpRequestMessage request, CancellationTokenSource cts, bool disposeCts) at async Task<TResponse> CommunicationWebClient.SendAsync<TResponse>(byte[] requestBytes, int offset, int count) at async Task<TResponse> CommunicationWebClient.SendWithRetryAsync<TResponse>(byte[] requestBytes, int offset, int count) at async Task<TResponse> CommunicationWebClient.SendAsync<TResponse>(IRequestWithResponse<TResponse> request) 2022-09-21 12:42:53.2810 Info Updater Sensor no value returned from SensorDeployment"; string pattern = @"^(\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]) (?:[01]?\d|2[0-3]):[0-5]?\d:[0-5]?\d\.\d{4} .*?)(\n(?!\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]) ).*)*$"; RegexOptions options = RegexOptions.Multiline; MatchCollection matches = Regex.Matches(logContent, pattern, options); int resultCount = 1; foreach (Match match in matches) { Console.WriteLine($"Result {resultCount}:"); Console.WriteLine($"主行内容: {match.Groups[1].Value}"); if (!string.IsNullOrEmpty(match.Groups[2].Value)) { Console.WriteLine("附加内容:"); string[] extraLines = match.Groups[2].Value.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string line in extraLines) { Console.WriteLine($" {line}"); } } Console.WriteLine("---"); resultCount++; } } }
代码说明
RegexOptions.Multiline:让^和$匹配每行的开头和结尾;- 拆分
Groups[2]内容为单独行输出,满足“换行内容单独展示”的需求。
简化版正则(仅提取完整条目)
若不需要单独分组附加内容,仅需提取完整条目,可使用更简洁的正则:
^\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]) .*?(?=\n\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]) |\z)
(?=\n\d{4}-... |\z)正前瞻:匹配到下一个日期行开头或日志结尾的位置,直接捕获完整条目。
内容的提问来源于stack exchange,提问作者squizzy
相关产品推荐
相关产品推荐

