在java regex中获取组名

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

例:

(?.*) 

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

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

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

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

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

捕获命名捕获组名称的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 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 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 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 getNamedGroupCandidates(String regex) { Set namedGroups = new TreeSet(); Matcher m = Pattern.compile("\\(\\?<([a-zA-Z][a-zA-Z0-9]*)>").matcher(regex); while (m.find()) { namedGroups.add(m.group(1)); } return namedGroups; } } } 

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

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

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

但请注意以下缺点

  • 如果代码在具有安全限制的系统中运行以拒绝任何尝试访问非公共方法(无修饰符,受保护和私有方法),则此方法可能不适用。
  • 该代码仅适用于Oracle或OpenJDK的JRE。
  • 由于我们正在调用非公共方法,因此代码也可能在将来的版本中中断。
  • 通过reflection调用函数也可能会影响性能。 (在这种情况下,性能损失主要来自reflection开销,因为在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 = "(?[az]*)[trick(?ha)]\\Q(?Q+E+)\\E(.*)(?\\w+)"; Pattern p = Pattern.compile(regex); Map 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 getNamedGroups(Pattern regex) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Method namedGroupsMethod = Pattern.class.getDeclaredMethod("namedGroups"); namedGroupsMethod.setAccessible(true); Map namedGroups = null; namedGroups = (Map) namedGroupsMethod.invoke(regex); if (namedGroups == null) { throw new InternalError(); } return Collections.unmodifiableMap(namedGroups); } } 

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

样品用法:

 Pattern p = Pattern.compile("(?.*)"); Matcher m = p.matcher("JohnDoe"); System.out.println(m.namedGroups()); // {user=JohnDoe} 

Maven的:

  com.github.tony19 named-regexp 0.2.3  

参考文献:

  • name-regexp 0.2.3
  • 匹配#namedGroups