Créer un tableau de correspondances regex

160

En Java, j'essaie de renvoyer toutes les correspondances regex dans un tableau mais il semble que vous ne puissiez vérifier si le modèle correspond à quelque chose ou non (booléen).

Comment puis-je utiliser une correspondance regex pour former un tableau de toutes les chaînes correspondant à une expression regex dans une chaîne donnée?

Jake Sankey
la source
2
Bonne question. Les informations que vous recherchez doivent faire partie de la documentation Java sur Regex et Matcher. Malheureusement, ce n'est pas le cas.
Cheeso
3
Une vraie honte. Cette fonctionnalité semble exister prête à l'emploi dans presque toutes les autres langues (prenant en charge les expressions régulières).
Ray Toal

Réponses:

278

( La réponse de 4castle est meilleure que celle ci-dessous si vous pouvez supposer que Java> = 9)

Vous devez créer un matcher et l'utiliser pour rechercher des correspondances de manière itérative.

 import java.util.regex.Matcher;
 import java.util.regex.Pattern;

 ...

 List<String> allMatches = new ArrayList<String>();
 Matcher m = Pattern.compile("your regular expression here")
     .matcher(yourStringHere);
 while (m.find()) {
   allMatches.add(m.group());
 }

Après cela, allMatchescontient les correspondances et vous pouvez utiliser allMatches.toArray(new String[0])pour obtenir un tableau si vous en avez vraiment besoin.


Vous pouvez également utiliser MatchResultpour écrire des fonctions d'assistance pour boucler les correspondances car Matcher.toMatchResult()renvoie un instantané de l'état actuel du groupe.

Par exemple, vous pouvez écrire un itérateur paresseux pour vous permettre de faire

for (MatchResult match : allMatches(pattern, input)) {
  // Use match, and maybe break without doing the work to find all possible matches.
}

en faisant quelque chose comme ça:

public static Iterable<MatchResult> allMatches(
      final Pattern p, final CharSequence input) {
  return new Iterable<MatchResult>() {
    public Iterator<MatchResult> iterator() {
      return new Iterator<MatchResult>() {
        // Use a matcher internally.
        final Matcher matcher = p.matcher(input);
        // Keep a match around that supports any interleaving of hasNext/next calls.
        MatchResult pending;

        public boolean hasNext() {
          // Lazily fill pending, and avoid calling find() multiple times if the
          // clients call hasNext() repeatedly before sampling via next().
          if (pending == null && matcher.find()) {
            pending = matcher.toMatchResult();
          }
          return pending != null;
        }

        public MatchResult next() {
          // Fill pending if necessary (as when clients call next() without
          // checking hasNext()), throw if not possible.
          if (!hasNext()) { throw new NoSuchElementException(); }
          // Consume pending so next call to hasNext() does a find().
          MatchResult next = pending;
          pending = null;
          return next;
        }

        /** Required to satisfy the interface, but unsupported. */
        public void remove() { throw new UnsupportedOperationException(); }
      };
    }
  };
}

Avec ça,

for (MatchResult match : allMatches(Pattern.compile("[abc]"), "abracadabra")) {
  System.out.println(match.group() + " at " + match.start());
}

rendements

a at 0
b at 1
a at 3
c at 4
a at 5
a at 7
b at 8
a at 10
Mike Samuel
la source
4
Je ne suggérerais pas d'utiliser une ArrayList ici car vous ne connaissez pas à l'avance la taille et voudrez peut-être éviter le redimensionnement de la mémoire tampon. Au lieu de cela, je préférerais une LinkedList - bien que ce ne soit qu'une suggestion et ne rende pas votre réponse moins valable.
Liv
13
@Liv, prenez le temps de comparer les deux ArrayListet LinkedList, les résultats peuvent être surprenants.
Anthony Accioly
J'entends ce que vous dites et je suis conscient de la vitesse d'exécution et de l'empreinte mémoire dans les deux cas; le problème avec ArrayList est que le constructeur par défaut crée une capacité de 10 - si vous dépassez cette taille avec des appels à ajouter ( ) vous devrez supporter l'allocation de mémoire et la copie du tableau - et cela peut arriver plusieurs fois. Certes, si vous vous attendez à quelques correspondances, votre approche est la plus efficace; si toutefois vous constatez que le "redimensionnement" du tableau se produit plus d'une fois, je suggérerais une LinkedList, encore plus si vous avez affaire à une application à faible latence.
Liv
12
@Liv, Si votre motif a tendance à produire des correspondances avec une taille assez prévisible, et selon que le motif correspond de manière clairsemée ou dense (en fonction de la somme des longueurs de allMatchesvs yourStringHere.length()), vous pouvez probablement précalculer une bonne taille pour allMatches. D'après mon expérience, le coût de la LinkedListmémoire et de l'efficacité des itérations n'en vaut généralement pas la peine, ce LinkedListn'est donc pas ma posture par défaut. Mais lors de l'optimisation d'un hot-spot, il vaut vraiment la peine d'échanger les implémentations de liste pour voir si vous obtenez une amélioration.
Mike Samuel
1
Dans Java 9, vous pouvez maintenant utiliser Matcher#resultspour obtenir un Streamque vous pouvez utiliser pour générer un tableau (voir ma réponse ).
4castle
56

Dans Java 9, vous pouvez maintenant utiliser Matcher#results()pour obtenir un Stream<MatchResult>que vous pouvez utiliser pour obtenir une liste / un tableau de correspondances.

import java.util.regex.Pattern;
import java.util.regex.MatchResult;
String[] matches = Pattern.compile("your regex here")
                          .matcher("string to search from here")
                          .results()
                          .map(MatchResult::group)
                          .toArray(String[]::new);
                    // or .collect(Collectors.toList())
4château
la source
1
leur méthode n'est pas de résultats (), veuillez exécuter ceci en premier
Bravo
14
@Bravo Utilisez-vous Java 9? Cela existe. J'ai lié à la documentation.
4castle
: ((y a-t-il une alternative pour java 8
logbasex
25

Java rend regex trop compliqué et ne suit pas le style perl. Jetez un œil à MentaRegex pour voir comment vous pouvez y parvenir en une seule ligne de code Java:

String[] matches = match("aa11bb22", "/(\\d+)/g" ); // => ["11", "22"]
MarchandJoeChicago
la source
6
C'est super. La double barre oblique a toujours l'air moche mais je suppose qu'il n'y a pas de scape de cela.
JohnPristine
mentaregex-0.9.5.jar, 6Ko qui m'a sauvé la journée, Obrigado Sérgio!
CONvid19
2
ATTENTION! La meilleure solution. Utilise le!
Vlad Holubiev
14
Le site MentaRegex est-il en panne? Lorsque je visite mentaregex.soliveirajr.com, il ne dit que "salut"
user64141
1
@ user64141 ressemble à ce qu'il est
Amit Gold
11

Voici un exemple simple:

Pattern pattern = Pattern.compile(regexPattern);
List<String> list = new ArrayList<String>();
Matcher m = pattern.matcher(input);
while (m.find()) {
    list.add(m.group());
}

(si vous avez plus de groupes de capture, vous pouvez y faire référence par leur index comme argument de la méthode group. Si vous avez besoin d'un tableau, utilisez list.toArray())

Bozho
la source
pattern.matches (entrée) ne fonctionne pas. Vous devez passer votre modèle regex (encore une fois!) -> WTF Java?! pattern.matches (String regex, String input); Voulez-vous dire pattern.matcher (entrée)?
El Mac
@ElMac Pattern.matches()est une méthode statique, vous ne devez pas l'appeler sur une Patterninstance. Pattern.matches(regex, input)est simplement un raccourci pour Pattern.compile(regex).matcher(input).matches().
dimo414
5

À partir des sentiers officiels Regex Java :

        Pattern pattern = 
        Pattern.compile(console.readLine("%nEnter your regex: "));

        Matcher matcher = 
        pattern.matcher(console.readLine("Enter input string to search: "));

        boolean found = false;
        while (matcher.find()) {
            console.format("I found the text \"%s\" starting at " +
               "index %d and ending at index %d.%n",
                matcher.group(), matcher.start(), matcher.end());
            found = true;
        }

Utilisez findet insérez le résultat groupdans votre tableau / liste / peu importe.

Anthony Accioly
la source
0
        Set<String> keyList = new HashSet();
        Pattern regex = Pattern.compile("#\\{(.*?)\\}");
        Matcher matcher = regex.matcher("Content goes here");
        while(matcher.find()) {
            keyList.add(matcher.group(1)); 
        }
        return keyList;
Nikhil Kumar K
la source