Java regex alternance de l'opérateur “|” comportement semble rompu
Essaie d'écrire une regex pour matcher chiffres romains. Dans le sed (qui, je pense, est considéré comme "standard" pour les regex?), si vous disposez de plusieurs options délimité par l'alternance de l'opérateur, elle correspond à la plus longue. À savoir, "I|II|III|IV"
correspondra à "IV" pour "IV" et "III" pour "III"
En Java, le même schéma correspond à "I" pour "IV" et "I" pour "III". S'avère Java choisit entre l'alternance des matchs de gauche à droite; c'est parce que "je" apparaît avant "III" dans la regex, ça correspond. Si je change la regex pour "IV|III|II|I"
, le problème est corrigé, mais ce n'est évidemment pas une solution en général.
Est-il un moyen de faire de Java choisir le plus long match sur une alternance de groupe, au lieu de choisir le "premier"?
Un exemple de code pour plus de clarté:
public static void main(String[] args)
{
Pattern p = Pattern.compile("six|sixty");
Matcher m = p.matcher("The year was nineteen sixty five.");
if (m.find())
{
System.out.println(m.group());
}
else
{
System.out.println("wtf?");
}
}
Ce sorties "six"
OriginalL'auteur Craig Kovatch | 2010-12-23
Vous devez vous connecter pour publier un commentaire.
Non, c'est de se comporter correctement. Java utilise une NFA, ou regex dirigée saveur, comme Perl, .NET, JavaScript, etc., et contrairement à sed, grep, ou awk. Un échange est prévu pour quitter dès que l'une des alternatives les matchs, de ne pas tenir pour le plus long match.
Vous pouvez le forcer à continuer en ajoutant une condition après l'alternance qui ne peuvent pas être respectées jusqu'à ce que le jeton a été consommé. Ce que cette condition peut être dépend du contexte; l'option la plus simple serait un point d'ancrage (
$
) ou à une limite de mot (\b
).EDIT: je dois mentionner que, bien que grep, sed, awk et d'autres traditionnellement utilisation de texte-dirigé (ou DFA) moteurs, vous pouvez également trouver des versions de certains d'entre eux qui utilisent l'ADN des moteurs, ou même des hybrides des deux.
Merci beaucoup. Je suis déçu, je ne peux pas la force de Java par défaut pour plus de match, mais tout à fait satisfait de l'explication de pourquoi, et les solutions de contournement que vous.
Il y a une brève description de regex dirigée vs texte dirigée moteurs de ici: regular-expressions.info/engine.html ...et Friedl il couvre très bien dans Le Livre: oreilly.com/catalog/regex3/index.html
Et oui, le tri des alternatives par ordre décroissant de longueur est une bonne pratique. Je vous recommande également de ne pas utiliser l'alternance à tous si il y a une autre option raisonnable. Par exemple, à l'aide de
six(?:ty)?
au lieu desix|sixty
ousixty|six
.Merci. Je sais que l'ADN contre DFA distinction, j'avais tout simplement jamais entendu parler de ces "regex dirigée" et "texte-réalisé" les termes utilisés pour eux avant. Savez-vous où ces conditions sont-elles originaires?
OriginalL'auteur Alan Moore
Je pense qu'un modèle qui va travailler est quelque chose comme
IV|I{1,3}
Voir le "glouton quantificateurs" à http://download.oracle.com/javase/1.4.2/docs/api/java/util/regex/Pattern.html
Edit: en réponse à votre commentaire, je pense que le problème général est que vous gardez à l'aide de l'alternance quand il n'est pas la bonne chose à utiliser. Dans votre nouvel exemple, vous essayez de faire correspondre le "six" ou "soixante"; le bon modèle à utiliser est
six(ty)?
, passix|sixty
. En général, si jamais vous avez deux membres d'une alternance de groupe tel que l'on est un préfixe d'un autre, vous devez réécrire l'expression régulière de l'éliminer. Sinon, vous ne pouvez pas vraiment se plaindre que le moteur est en train de faire la mauvaise chose, car la sémantique de l'alternance ne pas dire n'importe quoi sur un plus long match.Edit 2: le sens littéral de réponse à votre question est non, il ne peut pas être forcé (et mon commentaire est que vous ne devriez pas avoir besoin de ce comportement).
Edit 3: penser plus sur le sujet, il m'est apparu qu'un modèle de permutation où une chaîne est le préfixe d'un autre n'est pas souhaitable pour une autre raison, à savoir, il sera plus lent, à moins que le sous-jacent automate est construit à partir des préfixes en compte (et étant donné que Java prend le premier match dans le modèle, je suppose que ce n'est pas le cas).
C'est parce que vous gardez à l'aide de l'alternance lorsque vous ne voulez pas vraiment l'alternance. Je vais mettre à jour ma réponse à expliquer plus en détail.
La "norme de comportement" vous parlez, c'est un standard (POSIX) - à ma connaissance, c'est la seule norme qui exige que l'alternance de retour le plus long match. Étant donné qu'il n'y a rien de particulier à propos de l'alternance qui suggère que le plus long match est correct, il n'y a pas de raison de s'attendre à ce que l'arbitraire d'un algorithme de mise en correspondance doit choisir le plus long match.
Compte tenu de la regexp "six|soixante", si la règle est pas "le plus long match", il est difficile de voir ce que la règle serait. Si la règle était "plus courte match" ou "premier", "six|soixante" serait identique à "six", qui est juste stupide. Ce qui est à gauche?
J'espère que c'était évident que je critiquait le créateur et non la création.
OriginalL'auteur danben