De nombreuses implémentations modernes de regex interprètent le \w
raccourci 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Ä_432
ou gefräßig
.
Malheureusement, ce n'est pas le cas de Java. En Java, \w
est 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 \b
séparateur de mots correspond aux endroits où il ne devrait pas.
Quel serait l'équivalent correct d'un type .NET, compatible Unicode \w
ou \b
en Java? Quels autres raccourcis nécessitent une «réécriture» pour les rendre compatibles Unicode?
la source
Réponses:
Code source
Le code source des fonctions de réécriture dont je parle ci - dessous est disponible ici .
Mise à jour dans Java 7
La
Pattern
classe mise à jour de Sun pour JDK7 a un nouveau drapeau merveilleuxUNICODE_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 lesString
wrappers 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
,\d
et leurs compléments - ne sont pas en Java étendues à travailler avec Unicode. Seul parmi ceux-ci,\b
bénéficie de certaines sémantiques étendues, mais celles-ci ne correspondent\w
ni 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:
Ceci est un vrai gâchis, car cela signifie que des choses comme
Alpha
,Lower
etSpace
ne pas en Java carte à l'UnicodeAlphabetic
,Lowercase
ouWhitespace
proprié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:
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:
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:
Quelques points à considérer ...
Cela utilise pour sa
\X
dé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
\d
dé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,
\b
et\B
, sont spécifiquement écrites pour utiliser la\w
définition.Cette
\w
définition est trop large, car elle saisit les lettres rédigées et non seulement celles encerclées. LaOther_Alphabetic
proprié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
\b
et\B
pour en parler pour Perl 1.0 en 1987. La clé pour comprendre comment\b
et\B
à la fois le travail consiste à dissiper deux mythes envahissants à leur sujet:\w
caractères de mots, jamais des caractères autres que des mots.Une
\b
frontière signifie:Et ceux-ci sont tous définis de manière parfaitement simple comme:
(?<=\w)
.(?=\w)
.(?<!\w)
.(?!\w)
.Par conséquent, puisque
IF-THEN
est codé comme unand
ensembleAB
dans les expressions régulières, unor
estX|Y
, et parce que leand
est supérieur à la prioritéor
, c'est simplementAB|CD
. Donc, tout\b
cela signifie qu'une frontière peut être remplacée en toute sécurité par:avec le
\w
défini de la manière appropriée.(Vous pourriez trouver étrange que les composants
A
etC
soient opposés. Dans un monde parfait, vous devriez être capable de l'écrireAB|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
\B
non-frontières, la logique est:Permettre à toutes les instances de
\B
d'être remplacées par:C'est vraiment comment
\b
et\B
se comporter. Les modèles équivalents pour eux sont\b
utiliser la((IF)THEN|ELSE)
construction est(?(?<=\w)(?!\w)|(?=\w))
\B
utiliser la((IF)THEN|ELSE)
construction est(?(?=\w)(?<=\w)|(?<!\w))
Mais les versions avec juste
AB|CD
sont 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:
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:
(?:(?<=^)|(?<=\s))
(?=$|\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:
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'
\d
expansion, 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:
qui en Java vous écririez comme:
¡Tschüß!
la source
t
@tchrist. Cela pourrait me monter à la tête. :)C'est vraiment dommage que
\w
cela 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\w
devrait être[\p{L}\p{Digit}_]
.la source
\w
correspond également aux chiffres et plus encore. Je pense que pour juste des lettres,\p{L}
cela fonctionnerait.\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.\w
est défini par Unicode comme étant beaucoup plus large que juste\pL
et 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\w
pour Java - ou vous pouvez simplement utiliser maunicode_charclass
fonction à partir d' ici . Désolé!\pL
fonctionnent (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\pL
sous 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. :(En Java,
\w
et\d
ne 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 utiliseCharacter.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 ).la source
\b
est 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 leperl -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\b
en Java, alors tout cela fonctionne également en Java.\b
la justesse de 's, soulignant simplement qu'il fonctionne sur des caractères Unicode (tel qu'implémenté en Java), pas seulement comme ASCII\w
et ses amis. Cependant, cela fonctionne correctement en ce qui concerne le\u0301
moment où ce caractère est associé à un caractère de base, comme danse\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?\X
repré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\pM
au 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. […\b
soit partiellement compatible Unicode. Pensez à faire correspondre la chaîne"élève"
au modèle\b(\w+)\b
. Vous voyez le problème?\w+
trouve deux correspondances:l
etve
, ce qui est déjà assez mauvais. Mais avec les limites de mots, il ne trouve rien, car\b
reconnaîté
etè
comme caractères de mot. Au minimum,\b
et\w
devrait être d'accord sur ce qu'est un caractère de mot et ce qui ne l'est pas.