online - Obtener nombres de grupos en regex java
regex java 8 (3)
Desea utilizar la pequeña biblioteca name-regexp . Es una envoltura delgada alrededor de java.util.regex
con soporte de grupos de captura con nombre para usuarios de Java 5 o 6.
Uso de la muestra:
Pattern p = Pattern.compile("(?<user>.*)");
Matcher m = p.matcher("JohnDoe");
System.out.println(m.namedGroups()); // {user=JohnDoe}
Maven
<dependency>
<groupId>com.github.tony19</groupId>
<artifactId>named-regexp</artifactId>
<version>0.2.3</version>
</dependency>
Referencias:
Estoy intentando recibir un patrón y una cadena y devolver un mapa del nombre del grupo -> resultado coincidente.
Ejemplo:
(?<user>.*)
Me gustaría volver para un mapa que contenga "usuario" como clave y lo que coincida con su valor.
El problema es que parece que no puedo obtener el nombre del grupo de la API de expresiones regulares de Java. Solo puedo obtener los valores coincidentes por nombre o por índice. No tengo la lista de nombres de grupo y ni Pattern ni Matcher parecen exponer esta información. He comprobado su fuente y parece que la información está ahí, simplemente no está expuesta al usuario.
Probé java.util.regex y jregex de Java. (y realmente no importa si alguien sugirió alguna otra biblioteca que sea buena, con soporte y alta en términos de rendimiento que admita esta función).
Este es el segundo enfoque fácil del problema: llamaremos al método no público namedGroups()
en la clase Pattern para obtener un Map<String, Integer>
que mapea los nombres de los grupos a los números de grupo a través de la API Java Reflection . La ventaja de este enfoque es que no necesitamos una cadena que contenga una coincidencia con la expresión regular para encontrar los grupos nombrados exactos.
Personalmente, creo que no es una gran ventaja, ya que es inútil conocer los grupos nombrados de una expresión regular donde no existe una coincidencia con la expresión regular entre las cadenas de entrada.
Sin embargo, por favor tome nota de los inconvenientes :
- Es posible que este enfoque no se aplique si el código se ejecuta en un sistema con restricciones de seguridad para denegar cualquier intento de obtener acceso a métodos no públicos (sin métodos modificadores, protegidos y privados).
- El código solo se aplica a JRE desde Oracle o OpenJDK.
- El código también puede romperse en futuras versiones, ya que estamos llamando a un método no público.
- También puede haber un impacto en el rendimiento de la función de llamada a través de la reflexión. (En este caso, el impacto en el rendimiento proviene principalmente de la sobrecarga de reflexión, ya que no hay mucho que hacer con el método
namedGroups()
). No sé cómo el impacto en el rendimiento afecta el rendimiento general , así que haga una medición en su sistema.
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);
}
}
No hay API en Java para obtener los nombres de los grupos de captura nombrados. Creo que esta es una característica que falta.
La manera más fácil de hacerlo es elegir los grupos de captura nombrados candidatos del patrón, luego tratar de acceder al grupo nombrado desde el partido . En otras palabras, no sabe los nombres exactos de los grupos de captura nombrados, hasta que conecta una cadena que coincide con el patrón completo.
El Pattern
para capturar los nombres del grupo de captura con nombre es /(/?<([a-zA-Z][a-zA-Z0-9]*)>
(derivado de la documentación de la clase Pattern
).
(La manera más difícil es implementar un analizador para expresiones regulares y obtener los nombres de los grupos de captura).
Una implementación de muestra:
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;
}
}
}
Sin embargo, hay una advertencia a esta implementación. Actualmente no funciona con Pattern.COMMENTS
regulares en modo Pattern.COMMENTS
.