web-dev-qa-db-fra.com

java.util.regex - importance de Pattern.compile ()?

Quelle est l’importance de la méthode Pattern.compile()?
Pourquoi dois-je compiler la chaîne regex avant d’obtenir l’objet Matcher?

Par exemple :

String regex = "((\\S+)\\s*some\\s*";

Pattern pattern = Pattern.compile(regex); // why do I need to compile
Matcher matcher = pattern.matcher(text);
106
Sidharth

La méthode compile() est toujours appelée à un moment donné; c'est le seul moyen de créer un objet Pattern. La question est donc: pourquoi devriez-vous l'appeler explicitement? Une des raisons est que vous avez besoin d'une référence à l'objet Matcher pour que vous puissiez utiliser ses méthodes, comme group(int), pour extraire le contenu des groupes de capture. La seule façon d'obtenir un objet Matcher est d'utiliser la méthode matcher() de l'objet Pattern, et le seul moyen d'obtenir un objet Pattern est via la méthode compile(). Ensuite, il y a la méthode find() qui, contrairement à matches(), n'est pas dupliquée dans les classes String ou Pattern. 

L'autre raison est d'éviter de créer le même objet Pattern à plusieurs reprises. Chaque fois que vous utilisez l'une des méthodes régies par la regex dans String (ou la méthode statique matches() dans Pattern), un nouveau motif et un nouveau Matcher sont créés. Donc, cet extrait de code:

for (String s : myStringList) {
    if ( s.matches("\\d+") ) {
        doSomething();
    }
}

... est exactement équivalent à ceci:

for (String s : myStringList) {
    if ( Pattern.compile("\\d+").matcher(s).matches() ) {
        doSomething();
    }
}

De toute évidence, cela fait beaucoup de travail inutile. En fait, la compilation de l'expression rationnelle et l'instanciation de l'objet Pattern peuvent prendre plus de temps que pour effectuer une correspondance réelle. Il est donc généralement logique de sortir ce pas de la boucle. Vous pouvez aussi créer le Matcher à l'avance, même s'il n'est pas aussi coûteux:

Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher("");
for (String s : myStringList) {
    if ( m.reset(s).matches() ) {
        doSomething();
    }
}

Si vous connaissez les expressions rationnelles .NET, vous vous demandez peut-être si la méthode compile() de Java est liée au modificateur RegexOptions.Compiled de .NET; La réponse est non. La méthode Pattern.compile() de Java est simplement équivalente au constructeur Regex de .NET. Lorsque vous spécifiez l'option Compiled:

Regex r = new Regex(@"\d+", RegexOptions.Compiled); 

... il compile les expressions rationnelles directement dans le code d'octet CIL, ce qui lui permet de fonctionner beaucoup plus rapidement, mais à un coût considérable en traitement initial et en utilisation de la mémoire - considérez cela comme des stéroïdes pour les expressions rationnelles. Java n'a pas d'équivalent; il n'y a pas de différence entre un motif créé en coulisse par String#matches(String) et un modèle créé explicitement avec Pattern#compile(String).

(EDIT: A l'origine, j'avais dit que tous les objets .NET Regex étaient mis en cache, ce qui est incorrect. Depuis .NET 2.0, la mise en cache automatique ne se produit qu'avec des méthodes statiques telles que Regex.Matches(), et non lorsque vous appelez directement un constructeur Regex. ref )

130
Alan Moore

Compile analyse l'expression régulière et construit une représentation en mémoire . Les frais généraux à compiler sont importants comparés à une correspondance. Si vous utilisez un motif à plusieurs reprises , les performances du cache compilé seront plus performantes.

34
Thomas Jung

Lorsque vous compilez la variable Pattern, Java effectue des calculs afin de trouver plus rapidement les correspondances dans Strings. (Construit une représentation en mémoire de la regex)

Si vous envisagez de réutiliser plusieurs fois la Pattern, vous constaterez une augmentation considérable des performances par rapport à la création d'une nouvelle Pattern à chaque fois.

Dans le cas où vous n'utilisez le motif qu'une seule fois, l'étape de compilation apparaît simplement comme une ligne de code supplémentaire, mais peut s'avérer très utile dans le cas général.

16
jjnguy

C’est une question de performance et d’utilisation de la mémoire, compilez et conservez le modèle respecté si vous avez besoin de l’utiliser beaucoup ..__ Une utilisation typique de regex est de valider l’utilisateur input (format) , ainsi que les données de sortie pour les utilisateurs , dans ces classes, enregistrer le modèle respecté, semblent tout à fait logiques car elles appellent généralement beaucoup.

Voici un exemple de validateur, qui s’appelle vraiment beaucoup :) 

public class AmountValidator {
    //Accept 123 - 123,456 - 123,345.34
    private static final String AMOUNT_REGEX="\\d{1,3}(,\\d{3})*(\\.\\d{1,4})?|\\.\\d{1,4}";
    //Compile and save the pattern  
    private static final Pattern AMOUNT_PATTERN = Pattern.compile(AMOUNT_REGEX);


    public boolean validate(String amount){

         if (!AMOUNT_PATTERN.matcher(amount).matches()) {
            return false;
         }    
        return true;
    }    
}

Comme mentionné par @Alan Moore, si vous avez des expressions rationnelles réutilisables dans votre code (avant une boucle par exemple), vous devez compiler et enregistrer le modèle pour le réutiliser ultérieurement.

4
Alireza Fattahi

La classe de modèle est le point d'entrée du moteur de regex.Vous pouvez l'utiliser via Pattern.matches () et Pattern.comiple () . # Différence entre ces deux .matches () - pour vérifier rapidement si un texte (String) correspond à une expression régulière donnée comiple () - crée la référence de Pattern. Vous pouvez donc utiliser plusieurs fois pour faire correspondre l'expression régulière à plusieurs textes.

Pour référence:

public static void main(String[] args) {
     //single time uses
     String text="The Moon is far away from the Earth";
     String pattern = ".*is.*";
     boolean matches=Pattern.matches(pattern,text);
     System.out.println("Matches::"+matches);

    //multiple time uses
     Pattern p= Pattern.compile("ab");
     Matcher  m=p.matcher("abaaaba");
     while(m.find()) {
         System.out.println(m.start()+ " ");
     }
}
0
vkstream

La pré-compilation de la regex augmente la vitesse. Réutiliser le Matcher vous donne une autre légère accélération. Si la méthode est appelée fréquemment et qu'elle est appelée dans une boucle, la performance globale augmentera certainement.

0
DragonBorn

Pattern.compile() permet de réutiliser une expression rationnelle plusieurs fois (c'est threadsafe). L'avantage de la performance peut être assez important.

J'ai fait un repère rapide:

    @Test
    public void recompile() {
        var before = Instant.now();
        for (int i = 0; i < 1_000_000; i++) {
            Pattern.compile("ab").matcher("abcde").matches();
        }
        System.out.println("recompile " + Duration.between(before, Instant.now()));
    }

    @Test
    public void compileOnce() {
        var pattern = Pattern.compile("ab");
        var before = Instant.now();
        for (int i = 0; i < 1_000_000; i++) {
            pattern.matcher("abcde").matches();
        }
        System.out.println("compile once " + Duration.between(before, Instant.now()));
    }

compileOnce était compris entre x et 4x plus rapide. Je suppose que cela dépend fortement de la regex elle-même mais pour une regex qui est souvent utilisée, je vais pour un static Pattern pattern = Pattern.compile(...)

0
apflieger

Semblable à 'Pattern.compile', il y a 'RECompiler.compile' [de com.Sun.org.Apache.regexp.internal] où:
1. Le code compilé pour le modèle [a-z] contient «az»
2. Le code compilé pour le modèle [0-9] contient «09»
3. Le code compilé pour le motif [abc] contient "aabbcc".

Ainsi, le code compilé est un excellent moyen de généraliser plusieurs cas. Ainsi, au lieu d’avoir des situations de traitement de code différentes 1, 2 et 3. Le problème se réduit à une comparaison avec l’ascii des éléments présent et suivant dans le code compilé, d’où les paires. Ainsi
une. n'importe quoi avec ascii entre a et z est entre a et z
b. n'importe quoi avec ascii entre 'a et a est certainement' a '

0