6
votes

Regex - Obtenez une corde entre deux mots qui ne contiennent pas de mot

Je regarde autour de moi et je ne pouvais pas y arriver. Je ne suis pas totalement noob.

J'ai besoin d'obtenir du texte délimité par (y compris) début et fin qui ne contient pas de début. Fondamentalement, je ne trouve pas un moyen de nier un mot entier sans utiliser de choses avancées.

Exemple de chaîne:

abcstartabcstartabcendabc

Le résultat attendu:

startabcend

pas bon:

startabcstartabcend

Je ne peux pas utiliser de choses de recherche en arrière. Je teste ma regex ici: www.regexter.com

Merci pour tout conseil.


4 commentaires

Et si le texte est abcstartabcendabcstartabcendabc ? Voulez-vous les deux matchs?


N'ai-je pas pensé à ça ... Quoi qu'il en soit, je peux trouver deuxième match si nécessaire.


Mieux vaut faire cela dans une seule regex. J'ai ajouté une réponse.


Vous pouvez tester votre regex à Rubular.com


5 Réponses :


10
votes

Essayez ceci xxx

voir ici en ligne sur Regexr

(?!. * Démarrer) est un regard négatif. Il garantit que le mot "démarrage" ne suit pas

. *? est une correspondance non gourmande de tous les caractères jusqu'à la prochaine "fin". C'est nécessaire, parce que la lunette de regard négatif est juste à l'avenir et ne capturant rien (affirmation de longueur zéro)

mise à jour:

Je pensais un peu plus, la solution ci-dessus correspond à la première "FINIR". Si cela n'est pas recherché (parce que vous excluez le début du contenu), utilisez la version gourmande xxx

Ceci correspondra à la dernière "fin". < / p>


4 commentaires

+1 pour une bonne réponse avec des explications simples de tous les opérateurs


Cela échouera s'il y a plus d'un Démarrer ... fin paire dans la chaîne. (Ou plus précisément, il ne trouvera que le dernier Démarrer ... fin paire dans la chaîne.)


Pour clarifier le commentaire de Tim: votre regexp ne correspondra pas là où vous vous attendez à ce qu'il y a si il y a n'importe quel article deuxième occurrence de Démarrer , que ce soit avant ou < I> après fin (par exemple, abcstartabcendxyzstart ne correspondra pas)


Oui, cela demande simplement s'il y a une occurrence de départ à l'avenir et, dans l'affirmative, ne correspondra pas. Ce n'est pas le comportement recherché (décrit).



4
votes

La solution vraiment piétonne serait départ (([^ s] | s * s [^ st] | st [^ a] | sta [^ r] | star [^ t]) * (S ( T (ar?)?)?) Finalement . Les saveurs de regex modernes ont des assertions négatives qui le font plus élégamment, mais j'interprète votre commentaire sur la "recherche à l'envers" pour signifier peut-être que vous ne pouvez ni utiliser cette fonctionnalité.

Mise à jour : juste pour l'exhaustivité, notez que ce qui précède est gourmand en ce qui concerne le délimiteur de fin. Pour capturer uniquement la chaîne la plus courte possible, étendre la négation pour couvrir également le délimiteur d'extrémité - start ((([^ es] | e * e [^ ens] | EN [^ DS] | S * s [^ Ste ] | St [^ ae] | STA [^ re] | Star [^ te]) * (S (t (T (ar?)?)? | Fr?)?) Fin . Cela risque de dépasser le seuil de torture dans la plupart des cultures, cependant.

correction de bug: Une version précédente de cette réponse a eu un bogue, dans ce Sstart pourrait faire partie de la correspondance (le second s correspondrait à [^ t] , etc.). J'ai réparé cela mais par l'ajout de s dans [^ st] et ajout s * avant le non-optionnel s Pour permettre des répétitions arbitraires de S sinon.


8 commentaires

Belle solution (si pas de lunettes de lookaheads) +1


C'est ce que je cherchais, merci. En effet ... piéton :) mais ça marche. J'espérais qu'il pourrait y avoir une façon plus facile que je manque. Désolé de ne pas poster plus tôt.


Quelle est la dernière partie? Pourquoi avez-vous besoin (s (t (ar?)?)?)?


D'accord! Je l'obtiens ... Vous avez besoin de ... (s (t (t (ar?)?)?)? ... Parce que, autrement, vous devez consommer des caractères après s , st , STA et étoile ... Ceci est Freaking Genius.


Je ne sais pas ce que tu veux dire par ça. Une sous-chaîne de démarrage est autorisée avant le délimiteur de fin et de la manière dont nous avons empêché ces sous-chaînes de correspondance.


Je ne comprends pas la réponse. Ma question était pourquoi avez-vous besoin d'avoir cette partie (s (t (t (ar (ar?)?)?)?)? Mais je pense que la raison en est que vous ne correspondez pas à quelque chose comme StartStarend . Le (s (t (t (ar (ar?)?)?)? Consommez-vous de manière proprement toute sous-chaîne de étoile qui vient directement avant extrémité .


Oui, exactement. Plus tôt dans le match, nous autorisons star s'il est suivi de quelque chose qui n'est pas t , mais juste avant le délimiteur de fin, nous autorisons également Il doit être suivi de rien . (Utiliser "Consommer" dans ce contexte est un peu bizarre, IMHO, cependant.)


Merci de me pousser, je pense avoir trouvé un bug, bien que ce ne soit pas directement lié à cela. Je vais essayer de le réparer demain.



0
votes

[EDIT: J'ai quitté cet article pour les informations sur les groupes de capture, mais la solution principale que j'ai donnée n'était pas correcte. (? Démarrer) ((?: [^ S] | S [^ T] | ST [^ R] | STAR [^ T]) *) (?? ) Comme indiqué dans les commentaires ne fonctionnerait pas; J'oubliais que les personnages ignorés ne pouvaient pas être abandonnés et que vous auriez donc besoin de quelque chose comme ... | sta (?! [^ r]) | Pour toujours permettre à ce personnage de faire partie de la fin. , partant, échouant à quelque chose comme StartStaend; Donc, c'est clairement un meilleur choix; Les éléments suivants doivent indiquer la bonne façon d'utiliser les groupes de capture ...]

La réponse donnée à l'aide de l'opérateur "zéro-largeur négatif" "?!", avec des groupes de capture, c'est: (? Démarrer) ((?! * Démarrer). *) (? ) qui capture le texte interne en utilisant 1 $ pour le remplacement. Si vous souhaitez avoir les balises de démarrage et de fin capturées, vous pourriez faire (départ) ((? *. * Démarrer). *) (Fin) qui donne $ 2 = Texte ou diverses autres permutations en ajoutant / supprimer () s ou ?: s.

De cette façon si vous l'utilisez pour effectuer la recherche et le remplacer, vous pouvez le faire, quelque chose comme Begn $ 1inish. Donc, si vous avez commencé avec:

abcstartdefstarghiendjkl

Vous obtiendrez ghi comme groupe de capture 1 et remplaçant par Begin $ 1Finish vous donnerait les suivants:

abcstartdefbeginghifinishjkl

Qui vous permettrait de modifier vos jetons de démarrage / extrémité uniquement lorsqu'il est associé correctement.

chaque (x) est un groupe, mais j'ai mis (?: x) pour chacun des celles sauf le milieu qui la marque comme une non-capture grouper; le seul que j'ai laissé sans un ?: était le milieu; Cependant, vous pouvez également saisir éventuellement les jetons de début / fin si vous vouliez les déplacer ou quoi-vous-êtes.

Voir le Documentation Java Regex < / a> Pour plus de détails sur Java Regexes.


2 commentaires

Vous échouez sur le modèle STRATTAEND.


@tripleee soupir, oui, en effet et je devrais ignorer ces personnages avec ?! ce qui défait un peu le but. Je vous remercie de le faire remarquer.



6
votes
>>> re.findall(r"(?<=START)(?:(?!START).)*(?=END)", a)
['def', 'jlk', 'uvw']

1 commentaires

Yup, cela le fera. +1 (Bien que vous puissiez vouloir mentionner / utiliser le drapeau s dot-correspondant.)



2
votes

Puis-je suggérer une éventuelle amélioration de la solution de Tim Pietzcker? Il me semble que commencer (? :( ?! Démarrer).).) *? Fin est meilleur pour attraper un Démarrer immédiatement suivi d'un extrémité sans aucun Démarrer ou fin entre les deux. J'utilise la solution .NET et TIM correspondent également à quelque chose comme Démarrer la fin de fin . Au moins dans mon cas personnel, cela n'est pas recherché.


0 commentaires