正则表达式在Java中的命名组


172

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

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


3
您的理解不正确。JDK7处理命名组。
tchrist 2011年

2
@tchrist在2009年没有JDK7。
Alex78191

Answers:


274

更新2011年8月

正如geofflane回答中提到的那样,Java 7现在支持命名组
tchrist在评论中指出支持有限。
在出色的答案“ Java Regex Helper ”中详细说明了局限性

早在20109月,Oracle博客就提出了Java 7正则表达式(称为组支持)。

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

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

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


原始答案2009年1月,以下两个链接现在断开了)

您不能引用命名组,除非您编写自己的Regex版本。

这正是Gorbush2在此线程中所做的

正则表达式2

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

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

注意:您可以在Perl和PCRE正则表达式中找到真正的正则表达式递归示例,如Regexp PowerPCRE规范带有平衡括号的匹配字符串幻灯片中所述)

例:

串:

"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;
                }

上面的两个链接似乎都坏了?
乔纳斯(Jonas)2010年

此代码有错误。它正在寻找ASCII标识符。错了 它应该寻找Java允许在标识符中使用的任何内容!
tchrist 2011年

1
仅供参考,因为您似乎很尽职,所以关于ASCII与Uni​​code名称的限制并不多,因为每个名称只能具有一个命名组(您并不总是可以控制!),无法将它们用于正则表达式递归。
tchrist 2011年

@tchrist:谢谢您的精确(包括在内)。我还添加了指向您在“ Java Regex helper”上的出色回答的链接(已批准)。
VonC

Java中没有Matcher对象的matcher.name(int index)方法?
ot0


27

是的,但是它凌乱地侵入了阳光课。有一个更简单的方法:

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

named-regexp是标准JDK正则表达式实现的精简包装,其单一目的是处理.net样式的命名捕获组:(?...)。

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

Java 7将处理命名的捕获组,因此该项目不打算持续下去。


1
太糟糕了,不能在GWT内部使用。
Sakuraba,

4
请查看此项目的GitHub分支,该分支修复了原始项目中的一些错误。它也托管在Maven Central中。
tony19年

1
在我的情况下,请注意,Github上的tony19 fork在Android 0.1.8上不起作用。
Chuck D

2
@ RubberMallet,Android特定的问题现已修复,将在0.1.9中出现。
tony19


2

对于运行Java7之前的版本,joniOniguruma regexp库的Java端口)支持命名组。文档很少,但是对我们来说效果很好。
可以通过Maven(http://repository.codehaus.org/org/jruby/joni/joni/)获得二进制文件。


我对Ryan上面提到的joni选项非常感兴趣-您是否有使用命名捕获组的任何代码段-我已经设法获得基本的匹配并进行搜索以正常工作-但我不知道该使用哪种方法获得对groupNames的访问权或使用组名获取捕获的值。
malsmith

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.