【问题标题】:Regex to tokenize log line正则表达式标记日志行
【发布时间】:2021-06-07 22:16:09
【问题描述】:

我有一个日志行如下:

[2021-03-10 00:13:32.901] [DefaultDispatcher-worker-2 @coroutine#3] [DEBUG] [4231c006d9083a302fce59d5f0957226] [42c5ac3c0acfc68d] [GreeterImpl] 你好约翰

这是[] 中的 6 个文本块,然后是其余的。我正在寻找一个正则表达式来提取[] 中的文本,最后也是。 [] 中的文本块可以为空。

我尝试了(?:\[([^\[\]]*)\])+([^\[\]]+),但它只匹配[] 中的第一个块。我也尝试过(?:(?<=\[)[^\[\]]*(?=\]))+([^\[\]]+),但这不匹配任何东西。

FWIW,正则表达式将在 Java 中实现。

【问题讨论】:

    标签: java regex regex-lookarounds regex-group


    【解决方案1】:

    简短的编辑:这个稍微简单的正则表达式也可以:

    (?:(?<=\[)[^\[\]]*)|(?:(?<=\])[^\[\]]*$)
    

    我是从你自己的评论中摘录的。

    原始答案如下。

    TL;DR

    (?:(?<=^\[| \[)[^\[\]]*)|(?:(?<=\] )[^\[\]]*$)
    

    解释:有两个部分,用|,“或”隔开。

    1. 第一部分(?:(?&lt;=^\[| \[)[^\[\]]*) 匹配方括号内的内容。接近结尾处的[^\[\]]* 匹配既不是[ 也不是] 的最长可能字符序列。 (?&lt;=^\[| \[) 要求它前面有字符串的开头和 [ [。最后我把整个东西放到了一个非捕获组中,以确保后视优先于|
    2. 第二部分(?:(?&lt;=\] )[^\[\]]*$) 匹配日志行末尾方括号外的内容(示例中为Hello John)。这次非括号的运行必须以] 开头,然后是行尾。

    查看实际效果:

    1. On regex101 我建造它的地方

    2. 在 Java 中:

      String logLine = "[2021-03-10 00:13:32.901]"
              + " [DefaultDispatcher-worker-2 @coroutine#3] [DEBUG]"
              + " [4231c006d9083a302fce59d5f0957226] [42c5ac3c0acfc68d]"
              + " [GreeterImpl] Hello John";
      
      Matcher m = Pattern
              .compile("(?:(?<=^\\[| \\[)[^\\[\\]]*)|(?:(?<=\\] )[^\\[\\]]*$)")
              .matcher(logLine);
      while (m.find()) {
          System.out.println(m.group());
      }
      

    输出是:

    2021-03-10 00:13:32.901
    DefaultDispatcher-worker-2 @coroutine#3
    DEBUG
    4231c006d9083a302fce59d5f0957226
    42c5ac3c0acfc68d
    GreeterImpl
    Hello John
    

    不同的想法:String.split()

        String[] tokens = logLine.split("\\] \\[|\\] (?!\\[)");
        assert tokens[0].startsWith("[") : logLine;
        tokens[0] = tokens[0].substring(1);
    
        for (String token : tokens) {
            System.out.println(token);
        }
    

    输出和以前一样。

    我在] [] 分裂不是,然后是[(最后一次分裂)。它使第一个 [ 完好无损,所以我必须单独删除它,这不太好。否则我发现它比其他解决方案更容易理解。

    【讨论】:

    • 这是你在这里得到的一些严肃的正则表达式 foo :) 似乎(?:(?&lt;=\[)[^\[\]]*)|(?:(?&lt;=\])[^\[\]]*$) 稍微简单一些并且也可以工作。
    • 在我在这里询问之前,我的解决方法是简单地查找 [] 的索引,并提取其间的子字符串。当[ 的索引为-1 时,我们可以取字符串的其余部分。虽然代码稍微多一些(3 行?),但 IMO 它比拆分和删除第一个字符更简单。迄今为止提出的正则表达式解决方案优雅且学习良好。
    【解决方案2】:

    另一种选择是使用\G 锚来匹配开头的[...] 部分,以及可选的第2 组中的其余部分。

    这样您就可以区分方括号之间的部分和其余部分。

    \G\[([^][]*)]\h+([^][]+$)?
    

    模式匹配:

    • \G 将位置断言在上一个匹配的末尾或第一个匹配的字符串的开头
    • \[ 匹配[
    • ( 捕获第 1 组
      • [^][]* 匹配 0+ 次出现或除 [] 之外的任何字符
    • )关闭第一组
    • ]\h+ 匹配结束 ] 和 1 个或多个水平空白字符
    • ( 捕获第 2 组
      • [^][]+$ 匹配除 [] 之外的任何字符的 1+ 次出现
    • )? 关闭第 2 组并将其设为可选

    Regex demo | Java demo

    在带有双反斜杠的 Java 中

    String regex = "\\G\\[([^\\]\\[]*)]\\h+([^\\]\\[]+$)?";
    

    【讨论】:

    • 这与我最初尝试的很接近,除了我不知道的 \G 锚点。但是,我喜欢 Ole V.V.答案稍微好一点,因为他使用 OR 运算符,使意图更清楚地表明两组实际上不能共存。我赞成你抽出时间,并将我介绍给\G 主播。也就是说,我认为这两个答案也可以结合起来,但我不会那么努力。
    • @AbhijitSarkar 谢谢,没关系 :-) 顺便说一句,你可以缩短 Ole V.V. 的模式。通过删除像(?&lt;=\[)[^\[\]]*|(?&lt;=\])[^\[\]]*$ 这样的非捕获组作为一个小说明,仅使用交替也可能匹配此示例regex101.com/r/pVAJS8/1,其中使用\G 锚,字符串必须以开/闭方括号格式开头。见regex101.com/r/GoKYpA/1
    • 鉴于格式定义明确(它是日志行,不是用户提供的),我认为我们不需要担心像 ]test [test 这样的不匹配输入,但很高兴知道当然。
    猜你喜欢
    • 2019-02-09
    • 2021-11-09
    • 2018-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-10
    • 2018-08-11
    相关资源
    最近更新 更多