Java中的正则表达式命名组

Posted

技术标签:

【中文标题】Java中的正则表达式命名组【英文标题】:Regex Named Groups in Java 【发布时间】:2010-09-29 18:11:06 【问题描述】:

据我了解,java.regex 包不支持命名组 (http://www.regular-expressions.info/named.html),所以有人可以将我指向支持的第三方库吗?

我查看了jregex,但它的最后一个版本是在 2002 年,它在 java5 下对我不起作用(诚然我只是简单地尝试过)。

【问题讨论】:

你的理解有误。 JDK7 处理命名组。 @tchrist 2009 年没有 JDK7。 注意:这是discussed on Meta,12 年后。 【参考方案1】:

更新2011 年 8 月

正如geofflane 在his answer 中提到的那样,Java 7 now support named groups。tchrist 在评论中指出支持是有限的。 他在他的精彩回答“Java Regex Helper”中详细说明了局限性

September 2010 in Oracle's blog 中提供了 Java 7 正则表达式命名组支持。

在 Java 7 的正式版本中,支持命名捕获组的结构是:

(?<name>capturing text) 定义一个命名组“名称” \k<name> 反向引用命名组“名称” $name 引用 Matcher 替换字符串中捕获的组 Matcher.group(String name) 返回给定“命名组”捕获的输入子序列。

Java 7 之前的其他替代方案是:

Google named-regex(参见 John Hardy 的 answer)Gábor Lipták 提到(2012 年 11 月)该项目可能不活跃(使用 several outstanding bugs),可以考虑使用其 GitHub fork。 jregex(见Brian Clozel的answer)

原始答案2009 年 1 月,接下来的两个链接现已断开)

您不能引用命名组,除非您编写自己的正则表达式版本...

这正是Gorbush2 did in this thread。

Regex2

(有限的实现,正如tchrist 再次指出的那样,因为它只查找 ASCII 标识符。tchrist 详细说明了限制:

每个同名只能有一个命名组(您并不总是可以控制它!)并且不能将它们用于正则表达式内递归。

注意:您可以在 Perl 和 PCRE 正则表达式中找到真正的正则表达式递归示例,如 Regexp Power、PCRE specs 和 Matching Strings with Balanced Parentheses 幻灯片中所述)

例子:

字符串:

"TEST 123"

正则表达式:

"(?<login>\\w+) (?<id>\\d+)"

访问

matcher.group(1) ==> TEST
matcher.group("login") ==> TEST
matcher.name(1) ==> login

替换

matcher.replaceAll("aaaaa_$1_sssss_$2____") ==> aaaaa_TEST_sssss_123____
matcher.replaceAll("aaaaa_$login_sssss_$id____") ==> aaaaa_TEST_sssss_123____ 

(从实现中提取)

public final class Pattern
    implements java.io.Serializable

[...]
    /**
     * Parses a group and returns the head node of a set of nodes that process
     * the group. Sometimes a double return system is used where the tail is
     * returned in root.
     */
    private Node group0() 
        boolean capturingGroup = false;
        Node head = null;
        Node tail = null;
        int save = flags;
        root = null;
        int ch = next();
        if (ch == '?') 
            ch = skip();
            switch (ch) 

            case '<':   // (?<xxx)  look behind or group name
                ch = read();
                int start = cursor;
[...]
                // test forGroupName
                int startChar = ch;
                while(ASCII.isWord(ch) && ch != '>') ch=read();
                if(ch == '>')
                    // valid group name
                    int len = cursor-start;
                    int[] newtemp = new int[2*(len) + 2];
                    //System.arraycopy(temp, start, newtemp, 0, len);
                    StringBuilder name = new StringBuilder();
                    for(int i = start; i< cursor; i++)
                        name.append((char)temp[i-1]);
                    
                    // create Named group
                    head = createGroup(false);
                    ((GroupTail)root).name = name.toString();

                    capturingGroup = true;
                    tail = root;
                    head.next = expr(tail);
                    break;
                

【讨论】:

上面的两个链接好像都坏了? 这段代码有问题。它正在寻找 ASCII 标识符。这是错误的。它应该在标识符中查找 Java 允许的任何内容! 仅供参考,因为你看起来很认真,有限的部分不是关于 ASCII 与 Unicode 名称,而是关于每个相同名称只能有一个命名组(你没有t 始终可以控制!)并且无法将它们用于正则表达式内递归。 @tchrist:感谢您的精确度(包括在内)。我还添加了一个链接,返回您关于“Java Regex helper”的出色答案(已投票)。 Java 中没有 Matcher 对象的 matcher.name(int index) 方法??【参考方案2】:

是的,但是破解太阳课程很麻烦。有一个更简单的方法:

http://code.google.com/p/named-regexp/

named-regexp 是 标准 JDK 正则表达式 实施,与单 处理命名捕获的目的 .net 样式的组: (?...)。

它可以与 Java 5 和 6 一起使用 (使用泛型)。

Java 7 将处理命名捕获 组,所以这个项目并不意味着 持续下去。

【讨论】:

可惜不能在 GWT 中使用。 查看这个项目的GitHub fork,它修复了原来的几个错误。它也托管在 Maven Central 中。 请注意,Github 上的 tony19 fork 从 0.1.8 开始无法在 android 上运行。 @RubberMallet,Android 特有的问题现在是 fixed,并将在 0.1.9 中出现。【参考方案3】:

jregex 遇到了什么问题? 它在 java5 和 java6 下对我来说效果很好。

Jregex 做得很好(即使最后一个版本是 2002 年的),除非you want to wait for javaSE 7。

【讨论】:

【参考方案4】:

对于迟到的人:Java 7 添加了命名组。 Matcher.group(String groupName) documentation.

【讨论】:

【参考方案5】:

对于那些运行 pre-java7 的用户,joni(Oniguruma regexp 库的 Java 端口)支持命名组。文档很少,但对我们来说效果很好。 二进制文件可通过 Maven (http://repository.codehaus.org/org/jruby/joni/joni/) 获得。

【讨论】:

我对上面 Ryan 提到的 joni 选项非常感兴趣——你有任何使用命名捕获组的代码 sn-ps——我已经设法让基本匹配和搜索正常工作——但我看不到我将使用哪种方法来访问 groupNames 或使用组名获取捕获的值。【参考方案6】:

一个有点老的问题,但我发现自己也需要这个,并且上面的建议是不合适的 - 因此 - 我自己开发了一个薄包装:https://github.com/hofmeister/MatchIt

【讨论】:

以上是关于Java中的正则表达式命名组的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式中的命名捕获组

R中的正则表达式命名组

正则表达式匹配中的命名组

Python:使用JSON API链接通过正则表达式显示命名捕获组

正则表达式 - 仅当另一个命名组匹配时命名组

Boost 正则表达式:获取命名组