2009-06-22 6 views
0

Je travaille avec de longs paragraphes de texte qui sont consultables en utilisant MySQL et PHP. Je souhaiterais pouvoir rechercher et mettre en évidence uniquement les termes de recherche pertinents et utiliser regex pour les isoler.Extrait extraits avec PCRE regex

Par exemple, je voudrais transformer un paragraphe Lorem ipsum,

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor 
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud 
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor 
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur 
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est 
laborum. 

dans quelque chose comme ceci lors de la recherche pour "dolor",

Lorem ipsum *dolor* sit amet ... labore et *dolor*e magna aliqua ... aute irure *dolor* in reprehenderit ... esse cillum *dolor*e eu fugiat ... 

avec deux (ou cependant beaucoup) mots avant et après le terme.

Jusqu'à présent, j'ai ce

search - .*?(\w+?\b\s){2}(dolor)(\w+?\b\s){2}.*? 
replace - ... $1*$2*$3... 

mais il est pas tout à fait travailler; il ne trouve qu'un mot avant et après (malgré le {2}), échoue lorsque la chaîne de recherche est au début ou à la fin d'une chaîne (ou phrase), et n'élimine pas le reste du paragraphe après l'instance finale trouvée de la chaîne de recherche.

Quelle est la meilleure façon de faire cela?

Merci!

Répondre

1

A quelques changements:

((\w+\b\s*){2})(dolor)(\w*\s*(\w+\b\s*){2}) 

...$1*$3*$4... 

en premier lieu, le multiplicateur {2} doit être contenu dans la mémoire dans les deux cas, pour vous assurer souvenir les deux mots. Cela signifie que nous pouvons ignorer $2 lors de la relecture ($5 contient maintenant le dernier mot correspondant). Deuxièmement, dans le cas de "dolore" et de toute autre chose avec dolor \ w +, le terminal "e" devient un mot à part entière; Pour correspondre à votre spécification ci-dessus, j'ai ajouté \ w * \ s * pour piéger les caractères de fin de mot et les espaces terminaux dans le reste.

Sinon, le "?" Non gourmand Le char n'est pas vraiment nécessaire ici parce que vous spécifiez \ b à la fin de votre \ w +, donc j'ai aussi nettoyé ceux-ci.

+0

Brillant! C'est si proche ... Le seul problème est maintenant quand j'ajoute. *? Au début et à la fin de la recherche, tout ce qui n'est pas $ 1, $ 3 ou $ 4 est coupé (ce qui est bien) jusqu'au dernier groupe trouvé, quand il imprime juste le reste de la chaîne (pas bon) – Andrew

+0

I ' Je ne suis pas sûr que vous en ayez besoin! Vous n'avez pas d'ancre comme^ou $ là-dedans, donc ça va heureusement correspondre au milieu d'une chaîne. Cela signifie que vous n'avez pas vraiment besoin. * Sauf si vous souhaitez tout capturer. Est-ce que je manque quelque chose? –

+0

Ouais, je veux seulement sortir ... $ 1 $ 3 $ 4 ... - En ce moment, sauf si j'utilise. *, Le paragraphe entier est retourné avec les ellipses et les astérisques ajoutés – Andrew

0

En ce qui concerne le problème que seul un mot est adapté:

De la documentation PHP PCRE

Lorsqu'un sous-masque est répétée, la valeur capturée est la sous-chaîne qui correspondait à l'itération finale.

par exemple.

String 
"tweedledum tweedledee" 

Regex 
(tweedle[dume]{3}\s*)+ 

Captured value 
tweedledee 

Cette regex devrait vous permettre de vous rapprocher un peu plus.

.*?(\w+\b\s*\w+\b\s*)?(dolor)(\w*\s*\w+\b\s*\w+\b)?.*? 

Ne fonctionne pas pour dolor à la fin ou au début de la chaîne. Ne gère pas les caractères non spatiaux ou non. Ne gère pas le problème de plusieurs instances de dolor se suivant (par exemple dolor dolor dolor). Ne gère pas quand Dolor est dans le "2 mots sonné" (par exemple Lorem ipsum dolor amet dolor). D'autres cas spéciaux auxquels je ne peux pas penser maintenant ne sont pas non plus :-)

+0

Cela l'explique. Y a-t-il un moyen de contourner cela? – Andrew

+0

Ouais, cela fonctionne mieux, mais je n'aime vraiment pas les répétitions \ w +? \ B \ s * Hmmm ... – Andrew

+0

Amélioré avec le cas dolor \ w * \ s * – jitter

0

Il échoue au début/à la fin parce que vous spécifiez (ou du moins essayez de spécifier ...) qu'une correspondance doit inclure exactement deux mots de contexte menant et trailing. Si votre "dolor" est le premier mot, il n'y a rien avant, donc le match échoue. Changer le {2} à devrait réparer cette partie.

Une autre chose qui se démarque immédiatement est un peu votre utilisation de \w+?\b\s. Vous voulez probablement dire \w*\b\s. * signifie "correspondre à zéro ou plus", ce qui équivaut à "éventuellement correspondre à un ou plusieurs" que vous essayez de spécifier avec +?. Notez également que, sauf si vous modifiez \s en \s+, il échouera sur les mots séparés par plusieurs espaces. Il y a aussi des problèmes potentiels avec la ponctuation ou d'autres caractères qui ne sont ni des caractères de mot ni d'espace.En fin de compte, cependant, je pense que les expressions rationnelles ne sont peut-être pas la meilleure approche pour ce que vous essayez d'accomplir, ou du moins pas tout seul. Le moyen le plus efficace de le faire serait probablement de construire une recherche de texte intégral personnalisée avec l'index inverse contenant le texte du mot, sa position (afin que vous puissiez les obtenir dans le bon ordre), et le mot en surbrillance dans son contexte (vous pouvez donc les concaténer ensemble pour votre résultat final).

Si ce n'est pas une option, je vais diviser le texte en un tableau de mots, puis balayer à travers cela pour votre mot cible. Non seulement cela rendra plus facile la gestion de vos exigences de contexte, mais je m'attendrais à ce qu'il s'exécute aussi plus vite qu'une solution pure-regex, car cela réduirait considérablement le besoin potentiel de revenir en arrière. (OTOH, cependant, exécuter deux passages sur le texte (première passe pour le diviser en un tableau de mots, deuxième passage pour comparer chaque mot à vos termes de recherche) pourrait renverser les choses dans l'autre sens.)