正则表达式分组和可选匹配

5
首先:我并不擅长使用正则表达式。现在这一点已经摆在桌面上了。 我正在构建一个使用组和可选组件的正则表达式。问题在于,我需要匹配两个不同区域中的某个数字,并给它们相同的组名。但是这似乎行不通。
具体来说,我正在分析来自JVM的垃圾收集日志。这两行是一个完整的 GC 和一个普通的 GC。
为了便于阅读,我将它们分成了几行。
完整的行:
229980.058: [Full GC 229980.058: 
            [CMS: 2796543K->2796543K(2796544K), **13.3050667** secs]
            2983863K->2872464K(4067264K), 
            [CMS Perm : 325367K->325242K(1048576K)], 13.3054416 secs] 
            [Times: user=13.27 sys=0.03, real=13.31 secs] 

常规的代码行:

2.752: [GC 2.752: 
       [ParNew: 1143680K->4938K(1270720K), **0.0243534** secs] 
       1143686K->4945K(4067264K), 0.0245283 secs] 
       [Times: user=0.05 sys=0.02, real=0.03 secs] 

如您所见,Full GC 的第一个字段区域是CMS/tenured generation。第二个区域没有这些内容,只是常规集合。
为了捕获它们,我正确地使“CMS:”和“ParNew:”部分互相可选。然而,我想把每个部分的时间作为一个组名(在我加上**的值中)。
我使用以下正则表达式:
\d+.\d+: [(Full\s)?GC\s\d+.\d+: [(CMS:\s(?<JVM_TenuredGenHeapUsedBeforeGC>\d+)+K->(?<JVM_TenuredHeapUsedAfterGC>\d+)K(\d+K),\s(?<JVM_GCTimeTaken>\d+.\d+)\ssecs)? (ParNew:\s(?\d+)+K->(?<JVM_NewGenHeapUsedAfterGC>\d+)K((?<JVM_NewGenHeapSize>\d+)K),\s(?<JVM_GCTimeTaken>\d+.\d+)\ssecs)?] .. [已编辑以简洁为主]
简而言之...是否可能在不同的可选匹配项上使用相同的组名?它们永远不会在同一行上,所以我不知道为什么不能做到这一点。
使用regexr测试似乎也失败了。谢谢!
3个回答

3
一些实验表明,Java不允许在正则表达式中定义两次相同名称的捕获组。以下代码会生成以下异常:
public class NamedCapturingGroupMain {
    public static void main(String[] args) {
        Pattern p = Pattern.compile("(?<mygroup>a)|(?<mygroup>b)");
    }
}

异常:

Exception in thread "main" java.util.regex.PatternSyntaxException: Named capturing group <mygroup> is already defined near index 24

在这里最简单的方法可能是定义两个不同的捕获组名称,并且如果第一个为空,则使用第二个。例如,如果您使用了“JVM_GCTimeTakenFull”和“JVM_GCTimeTakenPartial”,然后执行以下操作:

String gcTimeTaken = matcher.group("JVM_GCTimeTakenFull");
if (gcTimeTaken == null) {
    gcTimeTaken = matcher.group("JVM_GCTimeTakenPartial");
}

我太懒了,不过Java 7支持重复的名称吗? - user557597
1
当您尝试使用我在答案中指定的非常简单的正则表达式进行“Pattern.compile”时,会出现PatternSyntaxException“已定义命名捕获组”的情况。 因此,我认为即使它们位于正则表达式的互斥分支中,Java 7也不支持重复名称。 - Mike Clark
1
天啊,Java什么时候才能做递归啊。我的意思是,他们必须得把头从屁股里拿出来了。 - user557597
@sln 我希望Java使用PCRE而不是移植IBM ICU。哦,算了。 - Mike Clark

3
我遇到的问题是需要在两个不同的区域匹配相同的数字,并给它们相同的组名。我认为这就是问题所在。我没有尝试过,但我看到修改列表介绍了命名组,那只是给一个编号的组命名。所以它行不通。给它们不同的名称并使用类似于
Objects.firstNonNull(m.group("foo"), m.group("bar"))

如果你确定它们中至少有一个不是null(否则你会得到一个NPE),那么可以使用该方法。或者编写自己的接受null的一行代码。


2

编辑 - 我错过了Java标签,如果Java不允许重复的名称(我知道它不支持分支重置),你可以这样做,然后测试匹配
Full_GCCMS(这样可以解释下一组内容)

无论哪种方式,您只需要一个JVM_GCTimeTaken组。

 # "\\d+\\.\\d+:\\s*\\[(?:(?<Full_GC>Full\\s*GC)|(?<GC>GC))\\s*(?<GC_Val>\\d+\\.\\d+):\\s*\\[(?:(?<CMS>CMS)|(?<ParNew>ParNew)):\\s*(?<HeapUsedBefore>\\d+)K->(?<HeapUsedAfter>\\d+)K\\((?<NewHeapSize>\\d+)K\\),\\s*(?<JVM_GCTimeTaken>\\d+\\.\\d+)\\s*secs\\]"


 \d+ \. \d+ : \s* 
 \[
     (?:
          (?<Full_GC> Full \s* GC )     # (1)
       |  (?<GC> GC )               # (2)
     )
     \s* 
     (?<GC_Val> \d+ \. \d+ )            # (3)
     : 
     \s* 
 \[
     (?:
          (?<CMS> CMS )                 # (4)
       |  (?<ParNew> ParNew )           # (5)
     )
     : \s* 
     (?<HeapUsedBefore> \d+ )           # (6)
     K->
     (?<HeapUsedAfter> \d+ )            # (7)
     K
     \(
     (?<NewHeapSize> \d+ )              # (8)
     K
     \)
     , \s* 
     (?<JVM_GCTimeTaken> \d+ \. \d+ )   # (9)
     \s* 
     secs
 \]

抱歉,我可能认为这是Dot-Net。对此感到抱歉。 - user557597
太好了!那非常有帮助。谢谢! - jgauthier
为Java解决方案编辑的帖子。 - user557597

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接