Obtenir les noms de groupe dans java regex


J'essaie de recevoir à la fois un motif et une chaîne et de renvoyer une carte de nom de groupe -> résultat correspondant.

Exemple:

(?<user>.*)

Je voudrais retourner une carte contenant "user" comme clé et tout ce qu'elle correspond comme valeur.

Le problème est que je n'arrive pas à obtenir le nom du groupe de l'api Java regex. Je ne peux obtenir les valeurs correspondantes que par nom ou par index. Je n'ai pas la liste des noms de groupe et ni Pattern ni Matcher ne semblent exposer ces informations. J'ai vérifié sa source et il semble que l'information soit là - elle n'est tout simplement pas exposée à l'utilisateur.

J'ai essayé les deux java de Java.util.regex et jregex. (et ne vous souciez pas vraiment si quelqu'un a suggéré une autre bibliothèque qui est bonne, prise en charge et élevée en termes de performances qui prennent en charge cette fonctionnalité).

Author: Roy Reznik, 2013-03-23

3 answers

Il n'y a pas d'API en Java pour obtenir les noms des groupes de capture nommés. Je pense que c'est une fonctionnalité manquante.

La solution la plus simple consiste à choisir des groupes de capture nommés candidats à partir du modèle, puis à essayer d'accéder au groupe nommé à partir de la correspondance. En d'autres termes, vous ne connaissez pas les noms exacts des groupes de capture nommés, jusqu'à ce que vous branchiez une chaîne qui correspond à l'ensemble du modèle.

Le Pattern pour capturer les noms du groupe de capture nommé est \(\?<([a-zA-Z][a-zA-Z0-9]*)> (dérivé basé sur Pattern documentation de classe).

(Le plus difficile est d'implémenter un analyseur pour regex et d'obtenir les noms des groupes de capture).

Un exemple d'implémentation:

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;
        }
    }
}

Il y a cependant une mise en garde à cette mise en œuvre. Il ne fonctionne actuellement pas avec regex dans Pattern.COMMENTS mode.

 36
Author: nhahtdh, 2015-12-17 02:45:45

C'est la deuxième approche facile du problème: nous appellerons la méthode non publique namedGroups() dans la classe Pattern pour obtenir un {[2] } qui mappe les noms de groupe aux numéros de groupe via API de réflexion Java. L'avantage de cette approche est que nous n'avons pas besoin d'une chaîne contenant une correspondance avec l'expression régulière pour trouver les groupes nommés exacts.

Personnellement, je pense que ce n'est pas vraiment un avantage, car il est inutile de connaître les groupes nommés d'une expression régulière où une correspondance avec le regex n'existe pas parmi les chaînes d'entrée.

Cependant, veuillez prendre note de les inconvénients:

  • Cette approche peut ne pas s'appliquer si le code est exécuté dans un système avec des restrictions de sécurité pour refuser toute tentative d'accès à des méthodes non publiques (pas de modificateur, méthodes protégées et privées).
  • Le code ne s'applique qu'à JRE depuis Oracle ou OpenJDK.
  • Le code peut également casser dans les versions futures, puisque nous appelons un non-public méthode.
  • Il peut également y avoir un impact sur les performances de la fonction d'appel via la réflexion. (Dans ce cas, la performance atteinte provient principalement de la surcharge de réflexion, car il ne se passe pas grand-chose dans la méthode namedGroups()). Je ne sais pas comment l'atteinte des performances affecte les performances globales, alors veuillez faire des mesures sur votre système.

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);
  }
}
 16
Author: nhahtdh, 2014-12-16 06:08:58

Vous voulez utiliser la petite bibliothèque name-regexp . Il s'agit d'un wrapper fin autour de java.util.regex avec prise en charge des groupes de capture nommés pour les utilisateurs Java 5 ou 6.

Exemple d'utilisation:

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>

Références:

 2
Author: Stephan, 2017-01-19 17:16:33