You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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]) ).*)*$

正则解析

  1. 第一分组(主行内容):(\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} .*?)
    • 匹配开头的日期时间行,用.*?非贪婪匹配到换行前的内容,避免过度覆盖后续条目。
  2. 第二分组(换行附加内容):(\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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 02:05:25