在java regex中获取组名

Roy*_*nik 29 java regex

我正在尝试接收模式和字符串并返回组名称的地图 - >匹配结果.

例:

(?<user>.*)
Run Code Online (Sandbox Code Playgroud)

我想返回一个包含"user"作为键的映射以及它匹配的值作为其值.

问题是我似乎无法从Java regex api获取组名.我只能按名称或索引获取匹配的值.我没有组名列表,Pattern和Matcher似乎都没有公开这些信息.我检查了它的来源,似乎信息就在那里 - 它只是没有暴露给用户.

我尝试了Java的java.util.regex和jregex.(并且不关心是否有人建议任何其他库,这些库是良好的,受支持的并且具有支持此功能的高性能).

nha*_*tdh 39

Java中没有API来获取指定捕获组的名称.我认为这是一个缺失的功能.

最简单的方法是从模式中挑选候选命名捕获组,然后尝试从匹配中访问命名组.换句话说,在插入与整个模式匹配的字符串之前,您不知道命名捕获组的确切名称.

Pattern捕捉名为捕获组的名称是\(\?<([a-zA-Z][a-zA-Z0-9]*)>(根据派生Pattern类的文档).

(困难的方法是为正则表达式实现解析器并获取捕获组的名称).

示例实现:

import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;
import java.util.Iterator;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.regex.MatchResult;

class RegexTester {

    public static void main(String args[]) {
        Scanner scanner = new Scanner(System.in);

        String regex = scanner.nextLine();
        StringBuilder input = new StringBuilder();
        while (scanner.hasNextLine()) {
            input.append(scanner.nextLine()).append('\n');
        }

        Set<String> namedGroups = getNamedGroupCandidates(regex);

        Pattern p = Pattern.compile(regex);
        Matcher m = p.matcher(input);
        int groupCount = m.groupCount();

        int matchCount = 0;

        if (m.find()) {
            // Remove invalid groups
            Iterator<String> i = namedGroups.iterator();
            while (i.hasNext()) {
                try {
                    m.group(i.next());
                } catch (IllegalArgumentException e) {
                    i.remove();
                }
            }

            matchCount += 1;
            System.out.println("Match " + matchCount + ":");
            System.out.println("=" + m.group() + "=");
            System.out.println();
            printMatches(m, namedGroups);

            while (m.find()) {
                matchCount += 1;
                System.out.println("Match " + matchCount + ":");
                System.out.println("=" + m.group() + "=");
                System.out.println();
                printMatches(m, namedGroups);
            }
        }
    }

    private static void printMatches(Matcher matcher, Set<String> namedGroups) {
        for (String name: namedGroups) {
            String matchedString = matcher.group(name);
            if (matchedString != null) {
                System.out.println(name + "=" + matchedString + "=");
            } else {
                System.out.println(name + "_");
            }
        }

        System.out.println();

        for (int i = 1; i < matcher.groupCount(); i++) {
            String matchedString = matcher.group(i);
            if (matchedString != null) {
                System.out.println(i + "=" + matchedString + "=");
            } else {
                System.out.println(i + "_");
            }
        }

        System.out.println();
    }

    private static Set<String> getNamedGroupCandidates(String regex) {
        Set<String> namedGroups = new TreeSet<String>();

        Matcher m = Pattern.compile("\\(\\?<([a-zA-Z][a-zA-Z0-9]*)>").matcher(regex);

            while (m.find()) {
                namedGroups.add(m.group(1));
            }

            return namedGroups;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,这个实现有一个警告.它目前无法在Pattern.COMMENTS模式下使用正则表达式.

  • +1,我会在正则表达式中将“+”更改为“*”,以匹配长度为1的组名。 (2认同)

nha*_*tdh 16

这是解决该问题的第二种简单方法:我们将namedGroups()在Pattern类中调用非公共方法,以Map<String, Integer>通过Java Reflection API获取将组名映射到组号的方法.这种方法的优点是我们不需要包含与正则表达式匹配的字符串来查找确切命名的组.

就个人而言,我认为它没有多大优势,因为知道正则表达式的命名组是无用的,其中输入字符串中不存在与正则表达式的匹配.

但请注意以下缺点:

  • 如果代码在具有安全限制的系统中运行,则拒绝任何尝试访问非公共方法(无修饰符,受保护和私有方法),则此方法可能不适用.
  • 该代码仅适用于Oracle或OpenJDK的JRE.
  • 由于我们正在调用非公共方法,因此代码也可能在将来的版本中中断.
  • 通过反射调用函数也可能会影响性能.(在这种情况下,性能损失主要来自反射开销,因为方法没有太多进展namedGroups()).我不知道性能影响如何影响整体性能,所以请在您的系统上进行测量.

import java.util.Collections;
import java.util.Map;
import java.util.Scanner;
import java.util.regex.Pattern;

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

class RegexTester {
  public static void main(String args[]) {
    Scanner scanner = new Scanner(System.in);

    String regex = scanner.nextLine();
    // String regex = "(?<group>[a-z]*)[trick(?<nothing>ha)]\\Q(?<quoted>Q+E+)\\E(.*)(?<Another6group>\\w+)";
    Pattern p = Pattern.compile(regex);

    Map<String, Integer> namedGroups = null;
    try {
      namedGroups = getNamedGroups(p);
    } catch (Exception e) {
      // Just an example here. You need to handle the Exception properly
      e.printStackTrace();
    }

    System.out.println(namedGroups);
  }


  @SuppressWarnings("unchecked")
  private static Map<String, Integer> getNamedGroups(Pattern regex)
      throws NoSuchMethodException, SecurityException,
             IllegalAccessException, IllegalArgumentException,
             InvocationTargetException {

    Method namedGroupsMethod = Pattern.class.getDeclaredMethod("namedGroups");
    namedGroupsMethod.setAccessible(true);

    Map<String, Integer> namedGroups = null;
    namedGroups = (Map<String, Integer>) namedGroupsMethod.invoke(regex);

    if (namedGroups == null) {
      throw new InternalError();
    }

    return Collections.unmodifiableMap(namedGroups);
  }
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*han 5

您想使用小的name-regexp库。它是一个瘦包装器java.util.regex,为 Java 5 或 6 用户提供命名捕获组支持。

示例用法:

Pattern p = Pattern.compile("(?<user>.*)");
Matcher m = p.matcher("JohnDoe");
System.out.println(m.namedGroups()); // {user=JohnDoe}
Run Code Online (Sandbox Code Playgroud)

马文:

<dependency>
  <groupId>com.github.tony19</groupId>
  <artifactId>named-regexp</artifactId>
  <version>0.2.3</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

参考:

  • 您是说 Java7+ 提供了此功能,但我找不到在 Java7 中获取命名组的方法。应该用什么? (2认同)
  • Matcher.group 采用组名作为参数。我不明白这如何帮助获取正则表达式的所有组名称;) (2认同)
  • @nhahtdh 我已经更新了删除无效 Java 7 引用的答案。 (2认同)