2010-01-02 10 views
5

À la suite d'un previous question où j'ai demandé:utilisant l'expression rationnelle pour correspondre à la chaîne entre deux chaînes en excluant les chaînes

Comment puis-je utiliser une expression régulière pour correspondre le texte qui se trouve entre deux chaînes, où les deux chaînes sont eux-mêmes enfermés deux autres chaînes, avec n'importe quelle quantité de texte entre les chaînes enfermantes intérieures et extérieures?

J'ai eu cette réponse:

/outer-start.*?inner-start(.*?)inner-end.*?outer-end/ 

Je voudrais maintenant savoir comment exclure certaines chaînes du texte entre les cordes enveloppantes extérieures et les cordes enveloppantes internes.

Par exemple, si j'ai ce texte:

externe-démarrage texte intérieur-début texte qui-i-want-extrémité intérieure texte plus outer -end

je voudrais « texte » et « un peu plus de texte » ne pas contenir le mot « indésirables ».

En d'autres termes, c'est OK:

externe-start certains voulaient le texte intérieur-start texte qui-i-want-extrémité intérieure un peu de texte plus recherché externe fin

Mais ce n'est pas OK:

externe-démarrage texte non désiré intérieur-démarragetexte qui-i-want-extrémité intérieure texte plus indésirable -extrémité extérieure

Ou d'expliquer davantage , l'expression entre les délimiteurs extérieurs et intérieurs dans la réponse précédente ci-dessus devrait exclure le mot «indésirable».

Est-ce facile à faire correspondre avec les regex?

+0

Qu'est-ce que vous essayez de le faire? – Gumbo

Répondre

5

Remplacez le premier et le dernier (mais pas le milieu) .*? par (?:(?!unwanted).)*?. (Où (?:...) est un groupe non-capture et (?!...) est une préanalyse négative.)

Cependant, cela se termine rapidement les cas de coin et mises en garde en tout réel (au lieu d'exemple) utiliser, et si vous poser des questions sur ce vous le faites vraiment (avec de vrais exemples, même s'ils sont simplifiés, au lieu d'exemples inventés), vous obtiendrez probablement de meilleures réponses.

+0

C'est une meilleure solution que la mienne. –

0

Essayez de remplacer le dernier. *? avec: (?! (. * texte non désiré. *))

Fonctionne-t-elle?

+1

Si vous n'êtes pas sûr (et même si vous pensez en être sûr), vous devriez tester votre modèle localement (ou sur un site comme http://codepad.org/), c'est pourquoi les questions regex ont besoin de bons exemples (les deux passer et échouer). –

1

Vous pouvez remplacer .*? avec

([^u]|u[^n]|un[^w]|unw[^a]|unwa[^n]|unwan[^t]|unwant[^e]|unwante[^d])*? 

Ceci est une solution dans regex "pure"; la langue que vous utilisez peut vous permettre d'utiliser des constructions plus élégantes.

1

Vous ne pouvez pas facilement faire cela avec des expressions régulières simples, mais certains systèmes tels que Perl ont des extensions qui le rendent plus facile. Une façon est d'utiliser une affirmation d'anticipation négative:

/outer-start(?:u(?!nwanted)|[^u])*?inner-start(.*?)inner-end.*?outer-end/ 

La clé est de diviser le « non désiré » dans (« u » non de « Nwanted ») ou (non « u »). Cela permet au motif d'avancer, mais il trouvera et rejettera toutes les chaînes "non désirées".

Les gens peuvent commencer à détester votre code si vous faites beaucoup de cela cependant. ;)

2

Une meilleure question à vous poser que "comment puis-je faire cela avec des expressions régulières?" est "comment puis-je résoudre ce problème?". En d'autres termes, ne vous attardez pas à essayer de résoudre un gros problème avec des expressions régulières. Si vous pouvez résoudre la moitié du problème avec des expressions régulières, faites-le, puis résolvez l'autre moitié avec une autre expression régulière ou une autre technique. Par exemple, faites un passage sur vos données pour obtenir toutes les correspondances, en ignorant le texte indésirable (lire: obtenir les résultats avec et sans le texte indésirable). Ensuite, passez un contrôle sur l'ensemble de données réduit et supprimez les résultats contenant le texte indésirable. Ce type de solution est plus facile à écrire, plus facile à comprendre et plus facile à maintenir dans le temps. Et pour tout problème que vous devrez probablement résoudre avec cette approche, il sera suffisamment rapide.

0

Tola, ressuscitant cette question car il avait une solution regex assez simple qui n'a pas été mentionnée. Ce problème est un cas classique de la technique expliquée dans cette question "regex-match a pattern, excluding..."

L'idée est de construire une alternance (une série de |) où les côtés gauche correspondent à ce que nous ne voulons pas afin de l'obtenir hors du chemin ... alors le dernier côté du | correspond à ce que nous voulons, et le capture au groupe 1. Si le groupe 1 est défini, vous le récupérez et vous avez une correspondance.

Alors qu'est-ce qu'on ne veut pas? Tout d'abord, nous voulons éliminer le bloc externe entier s'il y a unwanted entre outer-start et inner-start. Vous pouvez le faire avec:

outer-start(?:(?!inner-start).)*?unwanted.*?outer-end 

Ce sera à la gauche de la première |. Il correspond à un bloc externe entier. Deuxièmement, nous voulons éliminer tout le bloc extérieur s'il y a unwanted entre inner-end et outer-end. Vous pouvez le faire avec:

outer-start(?:(?!outer-end).)*?inner-end(?:(?!outer-end).)*?unwanted.*?outer-end 

Ce sera au milieu |.Cela semble un peu compliqué car nous voulons nous assurer que le "paresseux" *? ne saute pas à la fin d'un bloc dans un bloc différent. Troisièmement, nous faisons correspondre et capturer ce que nous voulons. C'est:

inner-start\s*(text-that-i-want)\s*inner-end 

Ainsi toute regex, en mode sans espacement, est:

(?xs) 
outer-start(?:(?!inner-start).)*?unwanted.*?outer-end # dont want this 
| # OR (also don't want that) 
outer-start(?:(?!outer-end).)*?inner-end(?:(?!outer-end).)*?unwanted.*?outer-end 
| # OR capture what we want 
inner-start\s*(text-that-i-want)\s*inner-end 

Sur this demo, consultez le groupe 1 des captures à droite: Il contient ce que nous voulons, et seulement pour le bon bloc.

En Perl et PCRE (utilisé par exemple en PHP), vous n'avez même pas à regarder le groupe 1: vous pouvez forcer l'expression régulière à ignorer les deux blocs que nous ne voulons pas. Le regex devient:

(?xs) 
(?: # non-capture group: the things we don't want 
outer-start(?:(?!inner-start).)*?unwanted.*?outer-end # dont want this 
| # OR (also don't want that) 
outer-start(?:(?!outer-end).)*?inner-end(?:(?!outer-end).)*?unwanted.*?outer-end 
) 
(*SKIP)(*F) # we don't want this, so fail and skip 
| # OR capture what we want 
inner-start\s*\Ktext-that-i-want(?=\s*inner-end) 

See demo: elle correspond directement ce que vous voulez.

La technique est expliquée en détail dans la question et l'article ci-dessous.

Référence