Equivalents Unicode pour \ w et \ b dans les expressions régulières Java?


126

De nombreuses implémentations modernes de regex interprètent le \wraccourci de classe de caractère comme «n'importe quelle lettre, chiffre ou ponctuation de connexion» (généralement: trait de soulignement). De cette façon, une expression régulière comme \w+matchs des mots comme hello, élève, GOÄ_432ou gefräßig.

Malheureusement, ce n'est pas le cas de Java. En Java, \west limité à [A-Za-z0-9_]. Cela rend difficile la correspondance des mots comme ceux mentionnés ci-dessus, entre autres problèmes.

Il semble également que le \bséparateur de mots correspond aux endroits où il ne devrait pas.

Quel serait l'équivalent correct d'un type .NET, compatible Unicode \wou \ben Java? Quels autres raccourcis nécessitent une «réécriture» pour les rendre compatibles Unicode?


3
La petite histoire, Tim, est qu'ils ont tous besoin d'écrire pour les mettre en conformité avec Unicode. Je ne vois toujours aucun signe que Java 1.7 fera autre chose avec les propriétés Unicode que d' ajouter finalement la prise en charge des scripts, mais c'est tout. Il y a certaines choses que vous ne pouvez vraiment pas faire sans un meilleur accès à l'ensemble des propriétés Unicode. Si vous n'avez pas encore mes scripts uniprops et unichars (et uninames ), ils vous ouvrent les yeux sur tout cela.
tchrist

On pourrait envisager d'ajouter des marques à la classe de mots. Puisque par exemple & auml; peut être représenté en Unicode sous la forme \ u0061 \ u0308 ou \ u00E4.
Mostowski Collapse

3
Hey Tim, regarde ma MISE À JOUR. Ils ont ajouté un drapeau pour que tout fonctionne. Hourra!
tchrist

Réponses:


240

Code source

Le code source des fonctions de réécriture dont je parle ci - dessous est disponible ici .

Mise à jour dans Java 7

La Patternclasse mise à jour de Sun pour JDK7 a un nouveau drapeau merveilleux UNICODE_CHARACTER_CLASS, qui fait que tout fonctionne à nouveau correctement. Il est disponible en tant qu'intégration (?U)à l'intérieur du motif, vous pouvez donc également l'utiliser avec les Stringwrappers de la classe. Il contient également des définitions corrigées pour diverses autres propriétés. Il suit maintenant le standard Unicode, à la fois dans RL1.2 et RL1.2a à partir de UTS # 18: Expressions régulières Unicode . Il s'agit d'une amélioration passionnante et spectaculaire, et l'équipe de développement doit être félicitée pour cet effort important.


Problèmes Regex Unicode de Java

Le problème avec Java Regexes est que les évasions charClass Perl 1.0 - ce qui signifie \w, \b, \s, \det leurs compléments - ne sont pas en Java étendues à travailler avec Unicode. Seul parmi ceux-ci, \bbénéficie de certaines sémantiques étendues, mais celles-ci ne correspondent \wni aux identificateurs Unicode , ni aux propriétés de saut de ligne Unicode .

De plus, les propriétés POSIX en Java sont accessibles de cette manière:

POSIX syntax    Java syntax

[[:Lower:]]     \p{Lower}
[[:Upper:]]     \p{Upper}
[[:ASCII:]]     \p{ASCII}
[[:Alpha:]]     \p{Alpha}
[[:Digit:]]     \p{Digit}
[[:Alnum:]]     \p{Alnum}
[[:Punct:]]     \p{Punct}
[[:Graph:]]     \p{Graph}
[[:Print:]]     \p{Print}
[[:Blank:]]     \p{Blank}
[[:Cntrl:]]     \p{Cntrl}
[[:XDigit:]]    \p{XDigit}
[[:Space:]]     \p{Space}

Ceci est un vrai gâchis, car cela signifie que des choses comme Alpha, Loweret Spacene pas en Java carte à l'Unicode Alphabetic, Lowercaseou Whitespacepropriétés. C'est extrêmement ennuyeux. La prise en charge de la propriété Unicode de Java est strictement antémillénaire , ce qui signifie qu'elle ne prend en charge aucune propriété Unicode qui est sortie au cours de la dernière décennie.

Ne pas pouvoir parler correctement des espaces est très ennuyeux. Considérez le tableau suivant. Pour chacun de ces points de code, il existe à la fois une colonne J-results pour Java et une colonne P-results pour Perl ou tout autre moteur regex basé sur PCRE:

             Regex    001A    0085    00A0    2029
                      J  P    J  P    J  P    J  P
                \s    1  1    0  1    0  1    0  1
               \pZ    0  0    0  0    1  1    1  1
            \p{Zs}    0  0    0  0    1  1    0  0
         \p{Space}    1  1    0  1    0  1    0  1
         \p{Blank}    0  0    0  0    0  1    0  0
    \p{Whitespace}    -  1    -  1    -  1    -  1
\p{javaWhitespace}    1  -    0  -    0  -    1  -
 \p{javaSpaceChar}    0  -    0  -    1  -    1  -

Regarde ça?

Pratiquement chacun de ces résultats d'espaces blancs Java est ̲w̲r̲o̲n̲g̲ selon Unicode. C'est un très gros problème. Java est juste foiré, donnant des réponses «fausses» selon la pratique existante et aussi selon Unicode. De plus, Java ne vous donne même pas accès aux véritables propriétés Unicode! En fait, Java ne prend en charge aucune propriété qui correspond à un espace blanc Unicode.


La solution à tous ces problèmes, et plus encore

Pour résoudre ce problème et bien d'autres problèmes connexes, j'ai écrit hier une fonction Java pour réécrire une chaîne de modèle qui réécrit ces 14 échappements charclass:

\w \W \s \S \v \V \h \H \d \D \b \B \X \R

en les remplaçant par des éléments qui fonctionnent réellement pour correspondre à Unicode de manière prévisible et cohérente. Ce n'est qu'un prototype alpha d'une seule session de piratage, mais il est complètement fonctionnel.

La petite histoire est que mon code réécrit ces 14 comme suit:

\s => [\u0009-\u000D\u0020\u0085\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]
\S => [^\u0009-\u000D\u0020\u0085\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]

\v => [\u000A-\u000D\u0085\u2028\u2029]
\V => [^\u000A-\u000D\u0085\u2028\u2029]

\h => [\u0009\u0020\u00A0\u1680\u180E\u2000-\u200A\u202F\u205F\u3000]
\H => [^\u0009\u0020\u00A0\u1680\u180E\u2000\u2001-\u200A\u202F\u205F\u3000]

\w => [\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]]
\W => [^\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]]

\b => (?:(?<=[\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])(?![\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])|(?<![\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])(?=[\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]]))
\B => (?:(?<=[\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])(?=[\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])|(?<![\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]])(?![\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]]))

\d => \p{Nd}
\D => \P{Nd}

\R => (?:(?>\u000D\u000A)|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029])

\X => (?>\PM\pM*)

Quelques points à considérer ...

  • Cela utilise pour sa \Xdéfinition ce que Unicode appelle maintenant un cluster de graphèmes hérité , et non un cluster de graphèmes étendu , car ce dernier est un peu plus compliqué. Perl lui-même utilise maintenant la version la plus sophistiquée, mais l'ancienne version est toujours parfaitement utilisable dans les situations les plus courantes. EDIT: voir addendum en bas.

  • Ce qu'il faut faire \ddépend de votre intention, mais la définition par défaut est la définition Uniode. Je peux voir des gens qui ne veulent pas toujours \p{Nd}, mais parfois l'un [0-9]ou l' autre \pN.

  • Les deux définitions de limites, \bet \B, sont spécifiquement écrites pour utiliser la \wdéfinition.

  • Cette \w définition est trop large, car elle saisit les lettres rédigées et non seulement celles encerclées. La Other_Alphabeticpropriété Unicode n'est pas disponible avant JDK7, c'est donc le mieux que vous puissiez faire.


Explorer les limites

Les frontières ont été un problème depuis que Larry Wall a inventé la première syntaxe \bet \Bpour en parler pour Perl 1.0 en 1987. La clé pour comprendre comment \bet\B à la fois le travail consiste à dissiper deux mythes envahissants à leur sujet:

  1. Ils ne recherchent jamais que des \wcaractères de mots, jamais des caractères autres que des mots.
  2. Ils ne recherchent pas spécifiquement le bord de la corde.

Une \bfrontière signifie:

    IF does follow word
        THEN doesn't precede word
    ELSIF doesn't follow word
        THEN does precede word

Et ceux-ci sont tous définis de manière parfaitement simple comme:

  • suit le mot est (?<=\w).
  • précède le mot est (?=\w).
  • ne suit pas le mot est (?<!\w).
  • ne précède pas le mot est (?!\w).

Par conséquent, puisque IF-THENest codé comme un and ensemble ABdans les expressions régulières, un orest X|Y, et parce que le andest supérieur à la priorité or, c'est simplement AB|CD. Donc, tout \bcela signifie qu'une frontière peut être remplacée en toute sécurité par:

    (?:(?<=\w)(?!\w)|(?<!\w)(?=\w))

avec le \wdéfini de la manière appropriée.

(Vous pourriez trouver étrange que les composants Aet Csoient opposés. Dans un monde parfait, vous devriez être capable de l'écrire AB|D, mais pendant un certain temps, j'ai recherché les contradictions d'exclusion mutuelle dans les propriétés Unicode - dont je pense avoir pris soin , mais j'ai laissé la condition double dans la limite au cas où. De plus, cela la rend plus extensible si vous obtenez des idées supplémentaires plus tard.)

Pour les \Bnon-frontières, la logique est:

    IF does follow word
        THEN does precede word
    ELSIF doesn't follow word
        THEN doesn't precede word

Permettre à toutes les instances de \Bd'être remplacées par:

    (?:(?<=\w)(?=\w)|(?<!\w)(?!\w))

C'est vraiment comment \bet \Bse comporter. Les modèles équivalents pour eux sont

  • \butiliser la ((IF)THEN|ELSE)construction est(?(?<=\w)(?!\w)|(?=\w))
  • \Butiliser la ((IF)THEN|ELSE)construction est(?(?=\w)(?<=\w)|(?<!\w))

Mais les versions avec juste AB|CDsont bien, surtout si vous manquez de modèles conditionnels dans votre langage regex - comme Java. ☹

J'ai déjà vérifié le comportement des limites en utilisant les trois définitions équivalentes avec une suite de tests qui vérifie 110385408 correspondances par exécution, et que j'ai exécutée sur une douzaine de configurations de données différentes selon:

     0 ..     7F    the ASCII range
    80 ..     FF    the non-ASCII Latin1 range
   100 ..   FFFF    the non-Latin1 BMP (Basic Multilingual Plane) range
 10000 .. 10FFFF    the non-BMP portion of Unicode (the "astral" planes)

Cependant, les gens veulent souvent une autre sorte de frontière. Ils veulent quelque chose qui tient compte des espaces et des bords de chaîne:

  • bord gauche comme(?:(?<=^)|(?<=\s))
  • bord droit comme(?=$|\s)

Correction de Java avec Java

Le code que j'ai publié dans mon autre réponse fournit cela et bien d'autres commodités. Cela inclut les définitions des mots en langage naturel, des tirets, des traits d'union et des apostrophes, ainsi qu'un peu plus.

Il vous permet également de spécifier des caractères Unicode dans des points de code logiques, et non dans des substituts UTF-16 idiots. Il est difficile de surestimer à quel point c'est important!Et ce n'est que pour l'extension des cordes.

Pour une substitution de classe de caractères regex qui fait que la classe de caractères de vos expressions régulières Java fonctionne enfin sous Unicode et fonctionne correctement, récupérez la source complète à partir d'ici . Vous pouvez en faire ce que vous voulez, bien sûr. Si vous y apportez des correctifs, j'aimerais en entendre parler, mais ce n'est pas obligatoire. C'est assez court. Les tripes de la fonction principale de réécriture de regex sont simples:

switch (code_point) {

    case 'b':  newstr.append(boundary);
               break; /* switch */
    case 'B':  newstr.append(not_boundary);
               break; /* switch */

    case 'd':  newstr.append(digits_charclass);
               break; /* switch */
    case 'D':  newstr.append(not_digits_charclass);
               break; /* switch */

    case 'h':  newstr.append(horizontal_whitespace_charclass);
               break; /* switch */
    case 'H':  newstr.append(not_horizontal_whitespace_charclass);
               break; /* switch */

    case 'v':  newstr.append(vertical_whitespace_charclass);
               break; /* switch */
    case 'V':  newstr.append(not_vertical_whitespace_charclass);
               break; /* switch */

    case 'R':  newstr.append(linebreak);
               break; /* switch */

    case 's':  newstr.append(whitespace_charclass);
               break; /* switch */
    case 'S':  newstr.append(not_whitespace_charclass);
               break; /* switch */

    case 'w':  newstr.append(identifier_charclass);
               break; /* switch */
    case 'W':  newstr.append(not_identifier_charclass);
               break; /* switch */

    case 'X':  newstr.append(legacy_grapheme_cluster);
               break; /* switch */

    default:   newstr.append('\\');
               newstr.append(Character.toChars(code_point));
               break; /* switch */

}
saw_backslash = false;

Quoi qu'il en soit, ce code est juste une version alpha, des trucs que j'ai piratés ce week-end. Cela ne restera pas ainsi.

Pour la version bêta, j'ai l'intention de:

  • pliez ensemble la duplication de code

  • fournir une interface plus claire concernant les échappements de chaîne sans échappement par rapport à l'augmentation des échappements de regex

  • fournir une certaine flexibilité dans l' \dexpansion, et peut-être\b

  • fournir des méthodes pratiques qui gèrent le retournement et l'appel de Pattern.compile ou String.matches ou autre chose pour vous

Pour la version de production, il devrait avoir javadoc et une suite de tests JUnit. Je peux inclure mon gigatester, mais ce n'est pas écrit comme des tests JUnit.


Addenda

J'ai de bonnes et de mauvaises nouvelles.

La bonne nouvelle est que j'ai maintenant une approximation très proche d'un cluster de graphèmes étendu à utiliser pour une amélioration \X.

La mauvaise nouvelle ☺ est que ce modèle est:

(?:(?:\u000D\u000A)|(?:[\u0E40\u0E41\u0E42\u0E43\u0E44\u0EC0\u0EC1\u0EC2\u0EC3\u0EC4\uAAB5\uAAB6\uAAB9\uAABB\uAABC]*(?:[\u1100-\u115F\uA960-\uA97C]+|([\u1100-\u115F\uA960-\uA97C]*((?:[[\u1160-\u11A2\uD7B0-\uD7C6][\uAC00\uAC1C\uAC38]][\u1160-\u11A2\uD7B0-\uD7C6]*|[\uAC01\uAC02\uAC03\uAC04])[\u11A8-\u11F9\uD7CB-\uD7FB]*))|[\u11A8-\u11F9\uD7CB-\uD7FB]+|[^[\p{Zl}\p{Zp}\p{Cc}\p{Cf}&&[^\u000D\u000A\u200C\u200D]]\u000D\u000A])[[\p{Mn}\p{Me}\u200C\u200D\u0488\u0489\u20DD\u20DE\u20DF\u20E0\u20E2\u20E3\u20E4\uA670\uA671\uA672\uFF9E\uFF9F][\p{Mc}\u0E30\u0E32\u0E33\u0E45\u0EB0\u0EB2\u0EB3]]*)|(?s:.))

qui en Java vous écririez comme:

String extended_grapheme_cluster = "(?:(?:\\u000D\\u000A)|(?:[\\u0E40\\u0E41\\u0E42\\u0E43\\u0E44\\u0EC0\\u0EC1\\u0EC2\\u0EC3\\u0EC4\\uAAB5\\uAAB6\\uAAB9\\uAABB\\uAABC]*(?:[\\u1100-\\u115F\\uA960-\\uA97C]+|([\\u1100-\\u115F\\uA960-\\uA97C]*((?:[[\\u1160-\\u11A2\\uD7B0-\\uD7C6][\\uAC00\\uAC1C\\uAC38]][\\u1160-\\u11A2\\uD7B0-\\uD7C6]*|[\\uAC01\\uAC02\\uAC03\\uAC04])[\\u11A8-\\u11F9\\uD7CB-\\uD7FB]*))|[\\u11A8-\\u11F9\\uD7CB-\\uD7FB]+|[^[\\p{Zl}\\p{Zp}\\p{Cc}\\p{Cf}&&[^\\u000D\\u000A\\u200C\\u200D]]\\u000D\\u000A])[[\\p{Mn}\\p{Me}\\u200C\\u200D\\u0488\\u0489\\u20DD\\u20DE\\u20DF\\u20E0\\u20E2\\u20E3\\u20E4\\uA670\\uA671\\uA672\\uFF9E\\uFF9F][\\p{Mc}\\u0E30\\u0E32\\u0E33\\u0E45\\u0EB0\\u0EB2\\u0EB3]]*)|(?s:.))";

¡Tschüß!


10
Ceci est incroyable. Merci beaucoup.
Tim Pietzcker

9
Christ, c'est une réponse éclairante. Je ne comprends pas la référence de Jon Skeet. Qu'est-ce qu'il a à voir avec ça?
BalusC

12
@BalusC: C'est un ref de Jon qui disait plus tôt qu'il me laisserait répondre à la question. Mais s'il vous plaît, ne laissez pas tomber t@tchrist. Cela pourrait me monter à la tête. :)
tchrist

3
Avez-vous pensé à l'ajouter à OpenJDK?
Martijn Verburg

2
@Martijn: Je ne l'avais pas fait, non; Je ne savais pas que c'était si «ouvert». :) Mais j'ai pensé à le publier dans un sens plus formel; d'autres dans mon département souhaitent que cela soit fait (avec une sorte de licence open source, probablement BSD ou ASL). Je vais probablement changer l'API de ce qu'elle est dans ce prototype alpha, nettoyer le code, etc. Mais cela nous aide énormément, et nous pensons que cela aidera aussi les autres. J'aimerais vraiment que Sun fasse quelque chose au sujet de leur bibliothèque, mais Oracle n'inspire aucune confiance.
tchrist

15

C'est vraiment dommage que \wcela ne fonctionne pas. La solution proposée \p{Alpha}ne fonctionne pas non plus pour moi.

Il semble [\p{L}]attraper toutes les lettres Unicode. Donc, l'équivalent Unicode de \wdevrait être [\p{L}\p{Digit}_].


Mais \wcorrespond également aux chiffres et plus encore. Je pense que pour juste des lettres, \p{L}cela fonctionnerait.
Tim Pietzcker

Vous avez raison. \p{L}est assez. Je pensais aussi que seules les lettres étaient le problème. [\p{L}\p{Digit}_]devrait attraper tous les caractères alphanumériques, y compris le trait de soulignement.
musique du

@MusicKk: Voir ma réponse pour une solution complète qui vous permet d'écrire vos modèles normalement, mais ensuite de le passer par une fonction qui corrige les lacunes béantes de Java afin qu'il fonctionne correctement sur Unicode.
tchrist

Non, \west défini par Unicode comme étant beaucoup plus large que juste \pLet les chiffres ASCII, de toutes les choses stupides. Vous devez écrire [\pL\pM\p{Nd}\p{Nl}\p{Pc}[\p{InEnclosedAlphanumerics}&&\p{So}]]si vous voulez un compatible Unicode \wpour Java - ou vous pouvez simplement utiliser ma unicode_charclassfonction à partir d' ici . Désolé!
tchrist

1
@Tim, oui, car les lettres \pLfonctionnent (vous n'avez pas besoin d'adopter des accessoires à une lettre). Cependant, vous le souhaitez rarement, car vous devez faire plutôt attention à ce que votre correspondance n'obtienne pas de réponses différentes simplement parce que vos données sont au format D de normalisation Unicode (alias NFD, ce qui signifie décomposition canonique ) par opposition à être en NFC (NFD suivi de canonique composition ). Un exemple est que le point de code U + E9 ( "é") est un \pLsous forme NFC, mais sa forme NFD devient U + 65.301, donc correspond \pL\pM. Vous pouvez un peu contourner ce avec \X: (?:(?=\pL)\X), mais vous aurez besoin de ma version de ce pour Java. :(
tchrist

7

En Java, \wet \dne sont pas compatibles avec Unicode; ils correspondent uniquement aux caractères ASCII [A-Za-z0-9_]et [0-9]. Il en va de même pour les \p{Alpha}amis (les «classes de caractères» POSIX sur lesquelles ils sont basés sont censées être sensibles aux paramètres régionaux, mais en Java, elles n'ont jamais fait correspondre que des caractères ASCII). Si vous voulez faire correspondre des «caractères de mot» Unicode, vous devez l'épeler, par exemple [\pL\p{Mn}\p{Nd}\p{Pc}]pour les lettres, les modificateurs sans espacement (accents), les chiffres décimaux et la ponctuation de connexion.

Cependant, Java \b est unicode-savvy; il utilise Character.isLetterOrDigit(ch)et vérifie également les lettres accentuées, mais le seul caractère de "ponctuation de connexion" qu'il reconnaît est le trait de soulignement. EDIT: quand j'essaye votre exemple de code, il s'imprime ""et élève"comme il se doit ( voir sur ideone.com ).


Je suis désolé, Alan, mais vous ne pouvez vraiment pas dire que Java \best un expert Unicode. Cela fait des tonnes et des tonnes d'erreurs. "\u2163=", "\u24e7="et "\u0301="tous échouent à correspondre au modèle "\\b="en Java, mais sont censés le faire - comme le perl -le 'print /\b=/ || 0 for "\x{2163}=", "\x{24e7}=", "\x{301}="'révèle. Cependant, si (et seulement si) vous permutez dans ma version d'une limite de mot au lieu du natif \ben Java, alors tout cela fonctionne également en Java.
tchrist

@tchrist: Je ne faisais pas de commentaire sur \bla justesse de 's, soulignant simplement qu'il fonctionne sur des caractères Unicode (tel qu'implémenté en Java), pas seulement comme ASCII \wet ses amis. Cependant, cela fonctionne correctement en ce qui concerne le \u0301moment où ce caractère est associé à un caractère de base, comme dans e\u0301=. Et je ne suis pas convaincu que Java soit faux dans ce cas. Comment une marque de combinaison peut-elle être considérée comme un caractère de mot si elle ne fait pas partie d'un groupe de graphèmes avec une lettre?
Alan Moore

3
@Alan, c'est quelque chose qui a été clarifié quand Unicode a clarifié les clusters de graphèmes en discutant des clusters de graphèmes étendus et hérités. L'ancienne définition d'un cluster de graphèmes, dans laquelle \Xreprésente une non-marque suivie d'un nombre quelconque de marques, est problématique, car vous devriez être en mesure de décrire tous les fichiers comme correspondant /^(\X*\R)*\R?$/, mais vous ne pouvez pas si vous avez un \pMau début de le fichier, ou même d'une ligne. Ils l'ont donc étendu pour qu'il corresponde toujours à au moins un caractère. Cela a toujours été le cas, mais maintenant, le modèle ci-dessus fonctionne. […
Suite

2
@Alan, cela fait plus de mal que de bien que le natif de Java \bsoit partiellement compatible Unicode. Pensez à faire correspondre la chaîne "élève"au modèle \b(\w+)\b. Vous voyez le problème?
tchrist

1
@tchrist: Oui, sans les limites du mot, \w+trouve deux correspondances: let ve, ce qui est déjà assez mauvais. Mais avec les limites de mots, il ne trouve rien, car \breconnaît éet ècomme caractères de mot. Au minimum, \bet \wdevrait être d'accord sur ce qu'est un caractère de mot et ce qui ne l'est pas.
Alan Moore
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.