繁体   English   中英

组数可变的正则表达式?

[英]Regular expression with variable number of groups?

是否可以创建具有可变数量组的正则表达式?

例如运行这个之后......

Pattern p = Pattern.compile("ab([cd])*ef");
Matcher m = p.matcher("abcddcef");
m.matches();

...我想要类似的东西

  • m.group(1) = "c"
  • m.group(2) = "d"
  • m.group(3) = "d"
  • m.group(4) = "c"

(背景:我正在解析一些数据行,其中一个“字段”在重复。我想避免对这些字段使用matcher.find循环。)


正如@Tim Pietzcker 在评论中指出的那样, perl6.NET具有此功能。

根据文档,Java 正则表达式不能这样做:

与组关联的捕获输入始终是该组最近匹配的子序列。 如果由于量化而对组进行第二次评估,那么如果第二次评估失败,则将保留其先前捕获的值(如果有)。 例如,将字符串“aba”与表达式 (a(b)?)+ 匹配,将第二组设置为“b”。 在每次匹配开始时,所有捕获的输入都将被丢弃。

(强调)

您可以使用 split 将您需要的字段放入数组并循环遍历。

http://download.oracle.com/javase/1,5.0/docs/api/java/lang/String.html#split(java.lang.String )

我没有使用过 java regex,但对于许多语言,答案是:不。

捕获组似乎在解析正则表达式时创建,并在匹配字符串时填充。 表达式(a)|(b)(c)具有三个捕获组,前提是可以填充其中一个或两个。 (a)*只有一个组,解析器匹配后留下组中的最后一个匹配项。

Pattern p = Pattern.compile("ab(?:(c)|(d))*ef");
Matcher m = p.matcher("abcdef");
m.matches();

应该做你想做的。

编辑:

@aioobe,我现在明白了。 你希望能够做一些类似语法的事情

A    ::== <Foo> <Bars> <Baz>
Foo  ::== "foo"
Baz  ::== "baz"
Bars ::== <Bar> <Bars>
        | ε
Bar  ::== "A"
        | "B"

并拉出Bar所有个人匹配项。

不,没有办法使用java.util.regex做到这一点。 您可以递归并在Bars的匹配上使用正则表达式,或者使用像 ANTLR 这样的解析器生成器并将副作用附加到Bar

我刚刚遇到了非常相似的问题,并设法做到了“可变数量的组”,但结合了 while 循环和重置匹配器。

    int i=0;
    String m1=null, m2=null;

    while(matcher.find(i) && (m1=matcher.group(1))!=null && (m2=matcher.group(2))!=null)
    {
        // do work on two found groups
        i=matcher.end();
    }

但这是针对我的问题(有两个重复

    Pattern pattern = Pattern.compile("(?<=^ab[cd]{0,100})[cd](?=[cd]{0,100}ef$)");
    Matcher matcher = pattern.matcher("abcddcef")
    int i=0;
    String res=null;

    while(matcher.find(i) && (res=matcher.group())!=null)
    {
        System.out.println(res);
        i=matcher.end();
    }

您无法使用*+指定任意长度的重复,因为前瞻和后视必须具有可预测的长度。

我认为回溯会抑制这种行为,并说/([\\S\\s])/在其分组累积状态下对圣经之类的东西的影响。 即使可以完成,输出也是不可知的,因为组将失去位置意义。 最好在全局意义上对同类进行单独的正则表达式并将其存放到数组中。

如果有合理的最大匹配组数,您会遇到:

"ab([cd])?([cd])?([cd])?([cd])?([cd])?([cd])?([cd])?([cd])?ef"

此示例适用于 0 - 8 个匹配项。 我承认这很丑陋,而且不是人类可读的。

我想避免对这些字段使用 matcher.find 循环。

正如其他答案中所述,这是无法避免的。 为了完整起见,这里是如何使用第二个Pattern到 go 来完成各个匹配项。 请注意*的 position 在圆括号内而不是之后。

Pattern subPattern = Pattern.compile("[cd]");
Pattern pattern = Pattern.compile("ab(" + subPattern.pattern() + "*)ef"); // DRY, but probably safer ways to do it for the case that subPattern needs to be changed.
Matcher matcher = pattern.matcher("abccdcddef is great and all, but have you heard about abef and abddcef?");
List<String> letterSequence = new ArrayList<>();
while (matcher.find()) {
    String letters = matcher.group(1);
    Matcher subMatcher = subPattern.matcher(letters);
    while (subMatcher.find()) {
        String letter = subMatcher.group();
        letterSequence.add(letter);
    }
}
System.out.println(letterSequence);

Output:

[c, c, d, c, d, d, d, d, c]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM