Guide pour échapper des caractères dans Java RegExps

Guide pour échapper des caractères dans Java RegExps

1. Vue d'ensemble

L'API d'expressions régulières en Java,java.util.regex est largement utilisée pour la correspondance de modèles. Pour en savoir plus, vous pouvez suivre cearticle.

Dans cet article, nous allons nous concentrer sur les caractères d'échappement dans une expression régulière et montrer comment cela peut être fait en Java.

2. Caractères RegExp spéciaux

Selon la documentation de l'API des expressions régulières Java, il existe un ensemble de caractères spéciaux, également appelés métacaractères, présents dans une expression régulière.

Lorsque nous voulons autoriser les personnages tels quels au lieu de les interpréter avec leurs significations spéciales, nous devons leur échapper. En évitant ces caractères, nous les forçons à être traités comme des caractères ordinaires lors de la correspondance d'une chaîne avec une expression régulière donnée.

Les métacaractères dont nous avons généralement besoin pour nous échapper sont les suivants:

<([\{\^-=$!|]})? +.> *

Examinons un exemple de code simple où nous faisons correspondre une entréeString avec un modèle exprimé dans une expression régulière.

Ce test montre que pour une chaîne d'entrée donnéefoof lorsque le motiffoo. (foo se terminant par un point) est mis en correspondance, il renvoie une valeur detrue qui indique que la correspondance est réussie.

@Test
public void givenRegexWithDot_whenMatchingStr_thenMatches() {
    String strInput = "foof";
    String strRegex = "foo.";

    assertEquals(true, strInput.matches(strRegex));
}

Vous pouvez vous demander pourquoi la correspondance est réussie quand il n'y a pas de caractère point (.) Présent dans l'entréeString?

La réponse est simple Le point (.) Est un métacaractère. L’importance particulière de ce point est qu’il peut y avoir «n'importe quel caractère» à la place. Par conséquent, il est clair comment le correspondant a déterminé qu’une correspondance est trouvée.

Disons que nous ne voulons pas traiter le caractère point (.) Avec sa signification unique. Au lieu de cela, nous voulons qu’il soit interprété comme un signe de point. Cela signifie que dans l'exemple précédent, nous ne voulons pas laisser le motiffoo. avoir une correspondance dans l'entréeString.

Comment pourrions-nous gérer une telle situation? La réponse est:we need to escape the dot (.) character so that its special meaning gets ignored.

Explorons-le plus en détail dans la section suivante.

3. Caractères en fuite

Selon la documentation de l'API Java pour les expressions régulières, il existe deux manières d'échapper aux caractères ayant une signification particulière. En d'autres termes, pour les forcer à être traités comme des caractères ordinaires.

Voyons ce qu'ils sont:

  1. Faites précéder un métacaractère d'une barre oblique inverse (\)

  2. Entourez un méta-caractère avec\Q et\E

Cela signifie simplement que dans l'exemple que nous avons vu précédemment, si nous voulons échapper au caractère point, nous devons placer un caractère barre oblique inverse avant le caractère point. Alternativement, nous pouvons placer le caractère de point entre \ Q et \ E.

3.1. Échapper à l'aide de la barre oblique inverse

C’est l’une des techniques que nous pouvons utiliser pour échapper aux métacaractères dans une expression régulière. Cependant, nous savons que la barre oblique inverse est également un caractère d'échappement dans les littéraux JavaString. Par conséquent, nous devons doubler la barre oblique inverse lorsque vous l'utilisez avant tout caractère (y compris le caractère \ lui-même).

Par conséquent, dans notre exemple, nous devons modifier l'expression régulière comme indiqué dans ce test:

@Test
public void givenRegexWithDotEsc_whenMatchingStr_thenNotMatching() {
    String strInput = "foof";
    String strRegex = "foo\\.";

    assertEquals(false, strInput.matches(strRegex));
}

Ici, le caractère de point est échappé. L’équipeur le traite simplement comme un point et essaie de trouver un motif qui se termine par le point (c.-à-d. foo.).

Dans ce cas, il renvoiefalse car il n'y a pas de correspondance dans l'entréeString pour ce modèle.

3.2. Échapper à l'aide de \ Q & \ E

Alternativement, nous pouvons utiliser\Q et\E pour échapper au caractère spécial. \Q indique que tous les caractères jusqu'à\E doivent être échappés et\E signifie que nous devons mettre fin à l'échappement qui a été commencé avec\Q.

Cela signifie simplement que tout ce qui se trouve entre\Q et\E serait échappé.

Dans le test présenté ici, lesplit() de la classeString fait une correspondance en utilisant l'expression régulière qui lui est fournie.

Notre exigence est de scinder la chaîne d'entrée par le caractère pipe (|) en mots. Par conséquent, nous utilisons un modèle d'expression régulière pour le faire.

Le caractère de pipe est un métacaractère qui doit être échappé dans l'expression régulière.

Ici, l'échappement se fait en plaçant le caractère pipe entre\Q et\E:

@Test
public void givenRegexWithPipeEscaped_whenSplitStr_thenSplits() {
    String strInput = "foo|bar|hello|world";
    String strRegex = "\\Q|\\E";

    assertEquals(4, strInput.split(strRegex).length);
}

4. La méthode Pattern.Quote (String S)

La méthode Pattern.Quote (String S) de la classejava.util.regex.Pattern convertit un modèle d'expression régulière donnéString en un modèle littéralString. Cela signifie que tous les métacaractères de l'entréeString sont traités comme des caractères ordinaires.

Utiliser cette méthode serait une alternative plus pratique que d'utiliser\Q &\E car elle enveloppe lesString donnés avec eux.

Voyons cette méthode en action:

@Test
public void givenRegexWithPipeEscQuoteMeth_whenSplitStr_thenSplits() {
    String strInput = "foo|bar|hello|world";
    String strRegex = "|";

    assertEquals(4,strInput.split(Pattern.quote(strRegex)).length);
}

Dans ce test rapide, la méthodePattern.quote() est utilisée pour échapper au modèle d'expression régulière donné et le transformer en un littéralString. En d'autres termes, il échappe pour nous à tous les métacaractères présents dans le motif de regex. Il fait un travail similaire à\Q et\E.

Le caractère pipe est échappé par la méthodePattern.quote() et lesplit() l'interprète comme un littéralString par lequel il divise l'entrée.

Comme nous pouvons le constater, cette approche est beaucoup plus propre et les développeurs ne doivent pas non plus se souvenir de toutes les séquences d'échappement.

5. Exemples supplémentaires

Voyons comment fonctionne la méthodereplaceAll() dejava.util.regex.Matcher.

Si nous devons remplacer toutes les occurrences d'un caractère donnéString par un autre, nous pouvons utiliser cette méthode en lui passant une expression régulière.

Imaginons que nous ayons une entrée avec plusieurs occurrences du caractère$. Le résultat que nous voulons obtenir est la même chaîne avec le caractère$ remplacé par £.

Ce test montre comment le modèle$ est passé sans être échappé:

@Test
public void givenRegexWithDollar_whenReplacing_thenNotReplace() {

    String strInput = "I gave $50 to my brother."
      + "He bought candy for $35. Now he has $15 left.";
    String strRegex = "$";
    String strReplacement = "£";
    String output = "I gave £50 to my brother."
      + "He bought candy for £35. Now he has £15 left.";

    Pattern p = Pattern.compile(strRegex);
    Matcher m = p.matcher(strInput);

    assertThat(output, not(equalTo(m.replaceAll(strReplacement))));
}

Le test affirme que$ n'est pas correctement remplacé par£.

Maintenant, si nous échappons au motif regex, le remplacement a lieu correctement et le test réussit comme le montre l'extrait de code suivant:

@Test
public void givenRegexWithDollarEsc_whenReplacing_thenReplace() {

    String strInput = "I gave $50 to my brother."
      + "He bought candy for $35. Now he has $15 left.";
    String strRegex = "\\$";
    String strReplacement = "£";
    String output = "I gave £50 to my brother."
      + "He bought candy for £35. Now he has £15 left.";
    Pattern p = Pattern.compile(strRegex);
    Matcher m = p.matcher(strInput);

    assertEquals(output,m.replaceAll(strReplacement));
}

Notez le\$ ici, qui fait l'affaire en échappant le caractère$ et en faisant correspondre le modèle avec succès.

6. Conclusion

Dans cet article, nous avons examiné les caractères d'échappement dans les expressions régulières en Java.

Nous avons discuté des raisons pour lesquelles les expressions régulières doivent être évitées et des différentes manières de les réaliser.

Comme toujours, le code source lié à cet article peut être trouvéover on GitHub.