124 votes

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

De nombreuses modernes regex implémentations interpréter l' \w classe de caractères sténographiques "toute lettre, chiffre, ou de la connexion de la ponctuation" (généralement: le trait de soulignement). De cette façon, une regex comme \w+ correspond à des mots comme hello, élève, GOÄ_432 ou gefräßig.

Malheureusement, Java ne fonctionne pas. En Java, \w est limitée à [A-Za-z0-9_]. Cela rend l'appariement des mots comme ceux mentionnés ci-dessus difficile, parmi d'autres problèmes.

Il apparaît également que l' \b séparateur de mots, des matchs dans des endroits où il ne devrait pas.

Quel serait le bon équivalent d'un .NET-comme, Unicode \w ou \b en Java? Qui d'autres raccourcis besoin de "réécriture" pour les rendre compatible avec Unicode?

238voto

tchrist Points 47116

EDIT: Le code source de la réécriture des fonctions que j'discuter ci-dessous est disponible ici.

Mise à JOUR: le Soleil de la mise à jour de Pattern classe pour JDK7 a un merveilleux nouveau drapeau, UNICODE_CHARACTER_CLASS, ce qui rend tout travail de nouveau à droite. Il est disponible comme intégrable (?U) pour l'intérieur du modèle, de sorte que vous pouvez l'utiliser avec l' String de la classe wrappers, trop. Il arbore également corrigé les définitions des différentes autres propriétés, trop. Il suit désormais Le Standard Unicode, dans les deux RL1.2 et RL1.2a partir de l'UT#18: Unicode des Expressions Régulières. C'est une période passionnante et dramatique de l'amélioration et le développement de l'équipe est à féliciter pour cet effort important.


Java de la Regex Unicode Problèmes

Le problème avec Java regexes est que le Perl 1.0 charclass échappe - sens \w, \b, \s, \d et leurs compléments, ne sont pas en Java étendu à travailler avec Unicode. Seul, parmi ces derniers, \b jouit d'une certaine étendue de la sémantique, mais ces carte ni d' \w, ni à Unicode identifiants, ni à Unicode ligne des propriétés de rupture.

En outre, la POSIX propriétés en Java sont accessibles de cette façon:

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}

C'est un véritable gâchis, car cela signifie que des choses comme Alpha, Lower, et Space ne pas en Java, carte de l'Unicode Alphabetic, Lowercaseou Whitespace propriétés. C'est exceeedingly ennuyeux. Java de la propriété Unicode support est strictement antemillennial, par qui je veux dire qu'il ne prend pas en charge Unicode propriété qui est sorti dans la dernière décennie.

Ne pas être capable de parler de les espaces correctement est super ennuyeux. Considérons le tableau suivant. Pour chacun de ces points de code, il est à la fois un J-résultats de la colonne pour Java, et un P-résultats de la colonne de Perl ou de tout autre PCRE à base de moteur d'expressions régulières:

             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  -

Voir qui?

Pratiquement tous ceux de Java espace blanc de résultats qui est faux selon Unicode. C'est un très gros problème. Java est juste foiré, en donnant des réponses qui sont "mauvais", selon la pratique existante et également en fonction de l'Unicode. En Plus de Java n'a même pas de vous donner l'accès à la vraie propriétés Unicode! En fait, Java ne supporte pas tout bien qui vous correspond à des espaces Unicode.


La Solution à Tous Ces Problèmes et bien Plus encore

Et de nombreux autres problèmes connexes, hier, j'ai écrit un Java de la fonction de réécriture d'une chaîne de modèle qui réécrit ces 14 charclass s'échappe:

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

pour les remplacer par des choses qui fonctionnent réellement pour correspondre à l'Unicode dans un prévisible et cohérente. C'est seulement un prototype alpha, à partir d'un seul hack de la session, mais il est complètement fonctionnel.

La petite histoire c'est que mon code réécrit ceux de 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*)

Certaines choses à considérer...

  • Qui utilise pour son \X définition de ce Unicode désigne maintenant comme un héritage de graphème cluster, pas une étendue de graphème cluster, que celui-ci est un peu plus compliqué. Perl utilise maintenant l'amateur version, mais l'ancienne version est toujours parfaitement réalisables pour la plupart des situations courantes. EDIT: Voir l'addendum au fond.

  • Que faire à propos de \d dépend de votre intention, mais la valeur par défaut est la Uniode définition. Je vois des gens pas toujours vouloir \p{Nd}, mais parfois [0-9] ou \pN.

  • Les deux limites des définitions, \b et \B, sont spécialement écrit pour l'utilisation de l' \w définition.

  • Qu' \w définition est trop large, car elle empoigne le parenned lettres et pas seulement le symbole. L'Unicode Other_Alphabetic de la propriété n'est pas disponible jusqu'à ce JDK7, de sorte que c'est le meilleur que vous pouvez faire.


L'Exploration Des Limites

Les frontières ont été un problème depuis que Larry Wall apparu pour la première fois l' \b et \B de la syntaxe pour parler d'eux pour Perl 1.0 en 1987. La clé pour comprendre comment \b et \B à la fois le travail est de dissiper deux omniprésente mythes à leur sujet:

  1. Ils sont toujours à la recherche pour \w caractères de mot, jamais pour les non-caractères de mot.
  2. Ils ne sont pas à l'affût pour de l'autre côté de la chaîne.

Un \b limite signifie:

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

Et ceux-ci sont tous parfaitement défini simplement comme:

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

Par conséquent, depuis IF-THEN est codé comme un and ed- AB dans les regexes, or est X|Y, et parce que l' and est plus élevé dans l'ordre de priorité qu' or, c'est tout simplement l' AB|CD. Ainsi, chaque \b ce qui signifie une limite peut être remplacé en toute sécurité avec:

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

avec l' \w défini de manière appropriée.

(Vous pourriez penser qu'il est étrange que l' A et C des composants sont opposés. Dans un monde idéal, vous devriez être en mesure d'écrire que AB|D, mais pendant un moment j'ai été chercher de l'exclusion mutuelle des contradictions dans les propriétés Unicode - que je pense que j'ai pris soin de, mais je l'ai laissé à la double condition à la limite juste au cas où. De Plus, ce qui le rend plus extensible si vous obtenez des idées plus tard.)

Pour l' \B non-frontières, la logique est:

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

En permettant à toutes les instances de l' \B à être remplacé par:

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

C'est vraiment la façon dont \b et \B se comporter. L'équivalent des modèles pour eux sont

  • \b à l'aide de l' ((IF)THEN|ELSE) construire est - (?(?<=\w)(?!\w)|(?=\w))
  • \B à l'aide de l' ((IF)THEN|ELSE) construire est - (?(?=\w)(?<=\w)|(?<!\w))

Mais les versions avec juste AB|CD sont très bien, surtout si vous n'avez pas conditionnelle des modèles dans vos regex langage comme Java. ☹

J'ai déjà vérifié le comportement des limites à l'aide de tous les trois définitions équivalentes avec une suite de test qui vérifie 110,385,408 matches par exécuter, et que j'ai exécuté sur une douzaine de différentes configurations de données 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, souvent, les gens veulent une autre sorte de frontière. Ils veulent quelque chose qui est des espaces et bord-de-la chaîne de conscience:

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

La fixation de Java avec Java

Le code que j'ai posté dans mon autre réponse, offre ce et de quelques autres commodités. Cela inclut des définitions en langage naturel des mots, des tirets, des traits d'union et apostrophes, et même un peu plus.

Il vous permet également de spécifier des caractères Unicode dans la logique des points de code, pas idiot UTF-16 mères porteuses. Il est difficile d'exagérer l'importance de cette! Et c'est juste pour de la chaîne d'extension.

Pour les regex charclass de substitution qui rend le charclass dans votre Java regexes enfin travailler sur Unicode, et de travailler correctement,de saisir la totalité du code source à partir d'ici. Vous pouvez le faire comme vous s'il vous plaît, bien sûr. Si vous faire des corrections, je serais ravi de les entendre, mais vous n'avez pas à. C'est assez court. Les entrailles de la principale regex réécriture de la fonction est simple:

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;

De toute façon, que le code est juste une version alpha, les trucs que j'ai piraté le week-end. Il ne reste pas de cette façon.

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

  • plier ensemble de la duplication de code

  • fournir une interface plus claire concernant unescaping chaîne échappe rapport à augmenter la regex s'échappe

  • offrir une certaine souplesse dans l' \d expansion, et peut-être l' \b

  • fournir la commodité des méthodes de la poignée en tournant autour et en matière d'appels.de la compilation ou de la Ficelle.les matchs ou autres joyeusetés pour vous

Pour la version de production, il devrait avoir javadoc et un test JUnit suite. Je peut comprendre mon gigatester, mais il n'est pas écrit comme JUnit tests.


Addendum

J'ai de bonnes nouvelles et de mauvaises nouvelles.

La bonne nouvelle est que maintenant j'ai une très bonne approximation pour une étendue de graphème cluster à utiliser pour une amélioration de l' \X.

La mauvaise nouvelle, c'est que le 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 devez écrire:

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üß!

15voto

musiKk Points 6933

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

Il semble que [\p{L}] attrape toutes les lettres Unicode. Ainsi, l'équivalent Unicode de \w devrait être [\p{L}\p{Digit}_] .

7voto

Alan Moore Points 39365

En Java, \w et \d ne sont pas Unicode; ils ne correspondent que les caractères ASCII, [A-Za-z0-9_] et [0-9]. Il en va de même pour l' \p{Alpha} et les amis (POSIX "les classes de personnage" elles sont basées sur sont censés être sensibles aux paramètres régionaux, mais en Java, ils ne se sont jamais égalé caractères ASCII). Si vous souhaitez faire correspondre Unicode "les caractères de mot", vous vous devez de le préciser, par exemple, [\pL\p{Mn}\p{Nd}\p{Pc}],pour les lettres, non-espacement des modificateurs (accents), les chiffres après la virgule, et la connexion de la ponctuation.

Cependant, Java \b est Unicode; il utilise Character.isLetterOrDigit(ch) et des contrôles pour les lettres accentuées, mais le seul "raccordement de la ponctuation", un personnage qu'il reconnaît est le trait de soulignement. EDIT: quand j'essaie votre exemple de code, il imprime "" et élève" comme il se doit (voir sur ideone.com).

0voto

Jon Skeet Points 692016

Peut-être utiliser [\p{Alpha}\p{Digit}_] pour \w ? Je ne sais pas trop quoi faire avec \b si ...

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