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
, Lowercase
ou 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:
- Ils sont toujours à la recherche pour
\w
caractères de mot, jamais pour les non-caractères de mot.
- 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üß!