#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 года, которая также не является ни техническим, ни спецификацией, ни справочным руководством, а скорее общим и, возможно, углубленным руководством по регулярным выражениям в целом.