web-dev-qa-db-fra.com

Comment trouver une dernière occurrence d'ensemble de caractères dans la chaîne à l'aide de Regex en Java?

J'ai besoin de trouver le dernier index d'un ensemble de caractères dans une chaîne. Considérez l'ensemble des caractères être x, y, z et chaîne comme Vereador Luiz pauz pauz home Puis j'ai besoin d'index comme 18.

Donc, pour trouver l'index, j'ai créé un motif avec [~ # ~] dotall [~ # ~ ~] Drapeau et quantificateur gourmand AS ((S). * (x | y | z. Lorsque le motif est appliqué à cette chaîne (multiligne), je peux découvrir l'index du groupe de démarrage. Le code:

int findIndex(String str){
  int index = -1;
  Pattern p = Pattern.compile("(?s).*(x|y|z)");
  Matcher m = regex.matcher(str);
  if(m.find()){
    index = m.start(1);
  }
  return index;
}

Comme prévu, il renvoie correctement les valeurs, s'il y a une correspondance.

Mais s'il n'y a pas de match, alors il faut trop de temps (17 minutes pour 600 000 caractères) car c'est un match gourmand.

J'ai essayé avec d'autres quantifiers, mais je ne peux pas obtenir la sortie souhaitée. Alors, quelqu'un peut-il renvoyer une meilleure regex?

PS: Je peux aussi penser à parcourir le contenu de la dernière et à la recherche de l'index. Mais j'espère qu'il y a une meilleure façon de réégalités qui peuvent faire le travail rapidement.

7
darklearner07

Il existe peu de façons de résoudre le problème et le meilleur moyen dépendra de la taille de l'entrée et de la complexité du motif:

  1. Inverser la chaîne d'entrée et éventuellement le motif, cela pourrait fonctionner pour des motifs non complexes. Malheureusement, Java.util.regex Ne permet pas de faire correspondre le motif de droite à gauche.

  2. Au lieu d'utiliser un quantificateur gourmand, faites simplement correspondre le motif et la boucle Matcher.find() jusqu'à ce que la dernière occurrence soit trouvée.

  3. Utilisez un moteur de regex différent avec une meilleure performance par exemple. RE2/J: Matching d'expression régulière de temps linéaire dans Java .

Si l'option 2 n'est pas suffisamment efficace pour votre cas, je suggère d'essayer RE2/J:

Le package d'expression régulier standard de Java, Java.Util.regex, et de nombreux autres emballages d'expression réguliers largement utilisés tels que PCRE, Perl et Python Utilisez une stratégie de mise en œuvre de retour en arrière: lorsqu'un modèle présente deux alternatives telles que: a|b, Le moteur essaiera de correspondre à Subppatn a d'abord, et si cela ne donne aucune correspondance, il réinitialisera le flux d'entrée et essayera de correspondre à b.

Si de tels choix sont profondément imbriqués, cette stratégie nécessite un nombre exponentiel de passes sur les données d'entrée avant de pouvoir détecter si l'entrée correspond à celle-ci. Si l'entrée est grande, il est facile de construire un motif dont le temps de fonctionnement dépasserait la durée de vie de l'univers. Cela crée un risque de sécurité lors de l'acceptation des modèles d'expression réguliers provenant de sources non approuvées, telles que les utilisateurs d'une application Web.

En revanche, l'algorithme RE2 explore toutes les correspondances simultanément dans une seule passe sur les données d'entrée en utilisant un automate fini non déterministe.

2
Karol Dowbecki

Stringbuilder a les deux a un reverse et est un charcuternence. La recherche est donc possible.

Pattern p = Pattern.compile("[xyz]");
StringBuilder sb = new StringBuilder(str).reverse();
Matcher m = p.matcher(sb);
return m.find() ? sb.length() - m.end() : -1;

Malheureusement, le renversement est coûteux.

Une solution sans regex est probablement plus rapide.

(Les paires de substitution BTW sont traitées correctement par le renversement.)

1
Joop Eggen