45 votes

Erreur de compilation d'une regex Java verbeuse avec classe de caractères et limite de mots

Pourquoi ce modèle ne compile-t-il pas ?

Pattern.compile("(?x)[ ]\\b");

Erreur

ERROR java.util.regex.PatternSyntaxException:
Illegal/unsupported escape sequence near index 8
(?x)[ ]\b
        ^
at java_util_regex_Pattern$compile.call (Unknown Source)

Alors que les équivalents suivants fonctionnent ?

Pattern.compile("(?x)\\ \\b");
Pattern.compile("[ ]\\b");
Pattern.compile(" \\b");

S'agit-il d'un bogue dans le compilateur regex de Java, ou ai-je manqué quelque chose ? J'aime utiliser [ ] en regex verbeux au lieu de backslash-backslash-space car cela permet d'économiser du bruit visuel. Mais apparemment, ce n'est pas la même chose !

PS : ce problème ne concerne pas les antislashes. Il s'agit de l'échappement des espaces dans une regex verbeuse en utilisant une classe de caractères contenant un seul espace. [ ] au lieu d'utiliser une barre oblique inversée.

D'une manière ou d'une autre, la combinaison de l'expression verbale de la géométrie (?x) et une classe de caractères contenant un seul espace [ ] perturbe le compilateur et l'empêche de reconnaître l'échappement de la frontière du mot. \b


Testé avec Java jusqu'à la version 1.8.0_151

6 votes

Non pas que cela résoudrait la question, mais en quoi une classe de caractères, contenant juste un espace, est-elle différente d'un espace littéral ?

4 votes

@userunknown : Le x (activé par l'option (?x) ) fait que les espaces blancs et les commentaires sont ignorés ; ainsi (?x)a b est équivalent à ab alors que (?x)a\ b est équivalent à a b . Comme l'explique Socowi dans sa réponse, le problème est que le PO s'attendait à ce que (?x)a[ ]b pour être équivalent à a[ ]b (c'est-à-dire à a b ), alors qu'en fait c'est équivalent à a[]b (qui n'est pas valide).

2 votes

@ruakh Exactement. Dans tous les autres moteurs PCRE [ ] est un moyen valide d'échapper aux espaces dans une regex verbeuse, voir par exemple Perl : echo 'a b' | perl -lne 'print if /a[ ]b/x' ou libpcre : echo 'a b' | pcregrep '(?x)a[ ]b'

31voto

Socowi Points 6380

J'aime utiliser [ ] en regex verbeux au lieu de backslash-backslash-space car cela permet d'économiser du bruit visuel. Mais apparemment, ce n'est pas la même chose !

"[ ]" est la même chose que "\\ " ou même " " .

Le problème est le suivant (?x) au début permettant mode commentaires . Comme le documentation États

Autorise les espaces et les commentaires dans le modèle.
Dans ce mode, les espaces blancs sont ignorés, et les commentaires intégrés commençant par avec # sont ignorés jusqu'à la fin d'une ligne.
Le mode commentaires peut également être activé via l'expression drapeau incorporée (?x) .

En mode commentaires, la regex "(?x)[ ]\\b" est la même chose que "[]\\b" et ne compilera pas parce que la classe de caractères vide [] n'est pas analysé comme vide, mais comme "[\\]" (classe de caractères non fermée contenant un littéral ] ).

Utilice " \\b" à la place. Vous pouvez également conserver l'espace en mode commentaires en l'échappant avec une barre oblique inverse : "(?x)[\\ ]\\b" o "(?x)\\ \\b" .

0 votes

Pourquoi alors l'espace blanc n'est pas ignoré en "(?x)\\ \\b" ?

7 votes

@SergiyKolesnikov Parce que les barres obliques inversées échappent à l'espace et l'empêchent d'être supprimé.

0 votes

@Socowi Vous devriez modifier votre réponse pour inclure votre commentaire.

22voto

ctwheels Points 13464

Il s'agit d'un bogue dans la fonction peekPastWhitespace() dans le Pattern classe. En retraçant tout ce problème... J'ai décidé de jeter un coup d'oeil à OpenJDK 8-b132's Pattern mise en œuvre . Commençons à le marteler depuis le début :

  1. compile() appelle expr() à la ligne 1696
  2. expr() appelle sequence() en ligne 1996
  3. sequence() appelle clazz() à la ligne 2063 puisque le cas de [ a été respecté
  4. clazz() appelle peek() à la ligne 2509
  5. peek() appelle peekPastWhitespace() à la ligne 1830 depuis if(has(COMMENTS)) évalue à true (du fait d'avoir ajouté le x drapeau (?x) au début du motif)
  6. peekPastWhitespace() (affiché ci-dessous) saute tous espaces dans le motif.

peekPastWhitespace()

private int peekPastWhitespace(int ch) {
    while (ASCII.isSpace(ch) || ch == '#') {
        while (ASCII.isSpace(ch))
            ch = temp[++cursor]
        if (ch == '#') {
            ch = peekPastLine();
        }
    }
    return ch;
}

Le même bug existe dans le parsePastWhitespace() méthode.

Votre expression est interprétée comme []\\b qui est la cause de votre erreur car \b n'est pas prise en charge dans une classe de caractères en Java. De plus, une fois que vous avez corrigé le \b votre classe de personnage n'a pas non plus de clôture. ] .

Ce que vous pouvez faire pour résoudre ce problème :

  1. \\ Comme l'OP l'a mentionné, utilisez simplement la double barre oblique inversée et l'espace.
  2. [\\ ] Échappez l'espace dans la classe de caractères pour qu'elle soit interprétée littéralement.
  3. [ ](?x)\\b Placez le modificateur en ligne après la classe de caractères

12voto

Pshemo Points 34648

Il semble qu'à cause de mode espacement libre (verbeux) (?x) espace dans [ ] est ignoré, donc le moteur de regex voit votre regex comme []\\b .
Si nous supprimons \\b il serait vu comme [] et nous obtiendrions l'erreur suivante Unclosed character class - La classe de caractères ne peut pas être vide, donc ] placé directement après [ est traité comme le premier caractère qui appartient à cette classe au lieu du méta-symbole qui ferme la classe de caractères.

Donc, puisque [ n'est pas fermée, le moteur regex voit \b comme étant placé à l'intérieur de cette classe de personnage. Mais \b ne peut pas être placé à cet endroit (il ne représente pas un caractère mais un "endroit") et nous voyons donc une erreur concernant "une séquence d'échappement non supportée" (dans la classe de caractères, mais cette partie a été ignorée).

En d'autres termes, vous ne pouvez pas utiliser [ ] pour échapper aux espaces en mode verbeux (du moins en Java). Vous devrez soit utiliser "\\ " o "[\\ ]" .

5voto

revo Points 5311

Une solution de contournement

En plus de l'échappement séparé des espaces blancs qui sont littéralement identiques à [ ] vous auriez pu x est activé pour l'ensemble de la regex, mais désactivé lorsque vous travaillez sur des motifs qui nécessitent des espaces, en ligne :

(?x)match-this-(?-x: with spaces )\\b
    ^^^^^^^^^^^     ^^^^^^^^^^^^^ ^^^
    `x` is on            off       on

ou une alternative serait d'utiliser les métacaractères de qouting \Q...\E :

(?x)match-this-\Q with s p a c e s \E\\b
    ^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^  ^^^
    `x` is on            off          on

Pourquoi un Exception ?

En mode étendu ou commentaire ( x ) les espaces blancs sont ignorés, mais la gestion des espaces dans les classes de caractères est différente.

Par exemple, dans le PCRE, tous les caractères d'espacement sont ignorés, sauf ceux d'une classe de caractères. Cela signifie que [ ] est une expression rationnelle valide, mais Java ne prévoit pas d'exception :

Dans ce mode, les espaces blancs sont ignorés...

Période. Donc, ceci [ ] est égal à ce [] qui n'est pas valide et génère un PatternSyntaxException exception.

Presque toutes les saveurs de regex, à l'exception de JavaScript, ont besoin d'une classe de caractères pour avoir au moins une unité de données. Ils traitent une classe de caractères vide comme un ensemble non fermé qui nécessite une parenthèse fermante. Cela dit, []] est valable dans la plupart des saveurs.

Mode d'espacement libre dans différentes versions sur [ ] :

  • PCRE valide
  • .NET valide
  • Perl valide
  • Ruby valide
  • TCL valide
  • Java 7 Invalide
  • Java 8 Invalide

5voto

YCF_L Points 31822

Analysons ce qui s'est passé exactement.

Jetez un coup d'œil au code source de java.util.regex.Pattern

Autorise les espaces et les commentaires dans le modèle. Dans ce mode, les espaces blancs sont ignorés, et les commentaires intégrés commençant par # sont ignorés jusqu'à la la fin d'une ligne.

Le mode commentaires peut également être activé via l'expression drapeau intégrée (?x).

Votre regex vous guide vers ceci ligne

private void accept(int ch, String s) {
    int testChar = temp[cursor++];
    if (has(COMMENTS))
        testChar = parsePastWhitespace(testChar);
    if (ch != testChar) {
        throw error(s);
    }
}

Si vous remarquez que votre code appelle parsePastWhitespace(testChar) ;

private int parsePastWhitespace(int ch) {
    while (ASCII.isSpace(ch) || ch == '#') {
        while (ASCII.isSpace(ch))//<----------------Here is the key of your error
            ch = temp[cursor++];
        if (ch == '#')
            ch = parsePastLine();
    }
    return ch;
}

Dans votre cas, vous avez un espace blanc dans votre expression régulière. (?x)[ ]\\b ceci retournera quelque chose (je ne peux pas l'analyser correctement) :

    if (ch != testChar) {
        throw error(s);
    }

qui n'est pas égal à ch et ici une exception est lancée

throw error(s);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X