.net模式:捕获多行日志文件中的字段

时间:2012-05-09 12:59:09

标签: .net regex

我有一个如下所示的日志文件:

2012-05-04 01:10:3​​5; 301383027; 00133608663205759673480010256592; 103; ERROR; AbstractTreatment:119; blah1

blah2

blah3

2012-05-02 01:00:22; 301382163; 00133591322220336011720010256592; 103; ERROR; AbstractTreatment:119; blah4

blah5

blah6

2012-05-02 01:00:23; 301382163; 00133591322220336011720010256592; 103; ERROR; AbstractTreatment:119; blah7

blah8

blah9

我希望每次有3个匹配的3个匹配组:日期,严重性和消息。

我试过使用这种模式

(20[0-9]{2}-[0-2][0-9]-[0-9]{2} [0-2][0-9]:[0-5][0-9]:[0-5][0-9]);[^;]*;[^;]*;[^;]*;([^;]*);(.*)

启用单行选项后,我有一个匹配(整个输入),如果禁用此选项,则不会完全捕获该消息(只有与日期在同一行的部分)。

我怎么能拥有与我想要正确捕获的3个值的日志条目一样多的匹配?

编辑: 我想捕捉这样的比赛:

日期: 2012-05-04 01:10:35

严重性:错误

消息: AbstractTreatment:119; blah1

blah2

blah3

2 个答案:

答案 0 :(得分:5)

这里有两个技巧。

  1. <强> “”不包括“\ n”,您无需设置 RegexOptions.Multiline。

  2. 您需要使用其他日期/时间模式或结束字符($)作为 分隔符,不应包含在匹配中。 (否则需要 在搜索之前从输入中排除分隔符 下一场比赛)。
    这需要使用特殊的分组表达式 用语法称为“零宽度正向前瞻断言” of(?= subexpression)。

  3. 要测试您的日志,我将其保存在“日志”设置变量中。

    string log = Settings.Default.Log;
    string datePattern = @"20[0-9]{2}-[0-2][0-9]-[0-9]{2} [0-2][0-9]:[0-5][0-9]:[0-5][0-9]";
    string pattern = @"(?<date>" + datePattern + @");[^;]*;[^;]*;[^;]*;(?<severity>[^;]*);(?<message>(.|\n)*?)(?=(" + datePattern + @"|$))";
    Match mtc = Regex.Match(log, pattern);
    
    while (mtc.Success)
    {
        Console.WriteLine("Date: " + mtc.Groups["date"].Value);
        Console.WriteLine("Severity: " + mtc.Groups["severity"].Value);
        Console.WriteLine("Message: " + mtc.Groups["message"].Value);
    
        mtc = mtc.NextMatch();
    }
    

    然后输出如下,

    Date: 2012-05-04 01:10:35
    Severity: ERROR
    Message: AbstractTreatment:119;blah1
    
    blah2
    
    blah3
    
    
    Date: 2012-05-02 01:00:22
    Severity: ERROR
    Message: AbstractTreatment:119;blah4
    
    blah5
    
    blah6
    
    
    Date: 2012-05-02 01:00:23
    Severity: ERROR
    Message: AbstractTreatment:119;blah7
    
    blah8
    
    blah9
    

答案 1 :(得分:2)

  Regex r = new Regex(
      @"^(?<date>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2});[^;]*;[^;]*;[^;]*;(?<severity>[^;]*);(?<message>.*(\n+(?!\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2};)[^\n]+)*)",
      RegexOptions.Multiline | RegexOptions.ExplicitCapture);

这个想法是,在.*消耗第一行的剩余部分之后,\n+消耗一个或多个行分隔符,而[^\n]+消耗下一个非空行的内容 - - 但前提 - (?!\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2};) - 无法匹配行开头的日期。 (因为,当然,以日期开头的行被假定为下一个日志条目的开头。)

Multiline选项会导致^锚点在换行符后以及字符串开头处匹配。 ExplicitCapture表示只捕获命名组,因此我不必使用(?:...)来阻止普通组捕获。每当使用命名组时,最好使用ExplicitCapture;命名和编号组在.NET正则表达式中奇怪地交互。