Извлекать имена групп захвата из строки регулярного выражения в Java?

#java #regex

Вопрос:

Я ищу быстрый способ извлечь имена групп захвата из любой строки регулярного выражения в Java. Если возможно, регулярное выражение, которое анализирует другие регулярные выражения, было бы решением, в противном случае «быстрый и грязный», но доказавший свою работоспособность анализатор также был бы решением. Если возможно, я хотел бы избежать внешних библиотек/зависимостей.

Например, учитывая входные данные:

 /(?<name>w ) (?<surname>w )/gm
 

Ожидаемый результат-массив/список/набор из 2 строк :

 String[] output = new String[]{"name", "surname"};
 

Я пытался использовать отражение, но безрезультатно.
При отладке объект шаблона содержит поле namedGroups, которое содержит нужную мне информацию, это карта, и меня интересует только набор ключей.

Я бы сам написал синтаксический анализатор, но я не могу найти документ с синтаксисом регулярных выражений Java.

 package my.package;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

import lombok.extern.slf4j.Slf4j;

@Slf4j // lombok declares slf4j static log variable
public class MyPatternUtils {

    @SuppressWarnings("unchecked")
    public static final Map<String, Integer> getNamedGroups(Pattern pattern) {
        if (pattern == null) {
            return Collections.unmodifiableMap(new HashMap<>(2));
        }

        // Cannot place class in java.util.regex package due to security exceptions
        // accessing methods with default access modifiers (compiles, but returns security exception on class loading)
//        try {
//            return Collections.unmodifiableMap(pattern.namedGroups());
//        } catch (Throwable t) {
//
//        }
//
//        try {
//            Map<String, Integer> namedGroups = pattern.namedGroups;
//            if (namedGroups == null) {
//                namedGroups = new HashMap<>(2);
//            }
//            return Collections.unmodifiableMap(namedGroups);
//        } catch (Throwable t) {
//
//        }

        try {
            Class<Pattern> patternClass      = Pattern.class;
            Method         namedGroupsMethod = patternClass.getMethod("namedGroups", new Class<?>[] {});
            namedGroupsMethod.setAccessible(true);
            Object namedGroupsObject = namedGroupsMethod.invoke(pattern, new Object[] {});
            return Collections.unmodifiableMap((Map<String, Integer>) namedGroupsObject);
        } catch (Throwable t) {
            log.error("err", t); // no such method
        }

        try {
            Class<Pattern> patternClass     = Pattern.class;
            Field          namedGroupsField = patternClass.getField("namedGroups");
            namedGroupsField.setAccessible(true);
            Object namedGroupsObject = namedGroupsField.get(pattern);
            if (namedGroupsObject == null) {
                namedGroupsObject = (Object) new HashMap<String, Integer>(2);
            }
            return Collections.unmodifiableMap((Map<String, Integer>) namedGroupsObject);
        } catch (Throwable t) {
            log.error("err", t); // no such field
        }

        return Collections.unmodifiableMap(new HashMap<>(2));
    }

}
 

Комментарии:

1. Я действительно не понимаю, что происходит в твоем MyPatternUtils классе. Но если все, что вам нужно, — это имена групп в массиве, разве не должно быть чего-то вроде String[] res = Pattern.compile("\?<(\w )\>").matcher(regex).results().map(mr -> mr.group(1)).toArray(String[]::new); достаточно?

2. Ну да, но есть и другие способы выражения групп, такие как /(?P<name>Sally)/ . Тогда возникает вопрос : если я перечислю все возможные способы именования групп, гарантированно ли этот метод будет работать (и соответствовать именам групп захвата) для всех возможных регулярных выражений ? И если да, то как это можно доказать ?

3. «Я не могу найти документ с синтаксисом регулярных выражений Java» Вы смотрели на javadoc java.util.regex.Pattern ?

4. Да, но, хотя это довольно полная документация по «использованию», я не уверен, что ее можно использовать в качестве ссылки для a parser implementation . В документации фактически говорится, что это краткое изложение, оно также ссылается на PERL 5 и книгу 2006 года, которая также не является ни техническим, ни спецификацией, ни справочным руководством, а скорее общим и, возможно, углубленным руководством по регулярным выражениям в целом.