2010-02-16 1 views
0

J'essaye d'écrire une regex qui va analyser les informations provenant des alertes générées par Hyperic HQ. Les alertes sont disponibles dans les courriels avec une ligne de sujet comme:Problème avec le look derrière l'assertion et la sous-chaîne optionnelle

"[HQ] !!! - Alert: My Demo Website Alert Resource: demo.myserver.net Apache Web Server State: fixed" 

Pour couper une longue histoire courte, je dois être en mesure de saisir systématiquement la partie « Apache Web Server », quel que soit le nom d'hôte qui ne peut même être présent. Je sais que le nom d'hôte finira toujours par "myserver.net".

Le regex J'est:

/Resource:\s.*(?<=mydomain.net)?\s(.*)\s(?=State)/ 

Je me attendais que cela correspondrait à zéro ou plusieurs caractères entre "Resource:" et "State:", suivant le cas échéant (à l'exclusion) un nom d'hôte.

Malheureusement, ce que cela renvoie est "Server", c'est-à-dire le dernier mot du bit que je veux faire correspondre. Cela se produit indépendamment du fait que le nom d'hôte figure dans la chaîne.

Quelqu'un peut-il aider?

EDIT: Solution telle que fournie par le Tchad ci-dessous

/Resource:\s(?:.*.myserver.net)?(.*)\sState/ 

Répondre

2

Cela semble fonctionner avec les tests que j'ai écrit

/Resource:\s(?:.*myserver.net)?(?<PartIWant>.*)\s(?:State)/ 

Il sera dans le groupe de capture nommé "PartIWant" si votre moteur de regex supporte les groupes de capture nommés.

EDIT: Je ai testé cette regex avec ces deux chaînes

[HQ] !!! - Alert: My Demo Website Alert Resource: demo.myserver.net Apache Web Server State: fixed 
[HQ] !!! - Alert: My Demo Website Alert Resource: Apache Web Server State: fixed 
+0

Télécharger Expresso, mettez toutes vos chaînes possibles dans les données de test, et d'exécuter cela et voir si elle correspond correctement – CaffGeek

+0

+1. Vous n'avez probablement pas besoin d'utiliser un lookahead pour "State" non plus. –

+0

@Alan, n'a même pas remarqué que, vous avez raison, il n'a pas besoin d'être là – CaffGeek

2

Voici un exemple de l'anti-modèle que j'appelle un recours prématuré à lookaround. Vous savez la chaîne que vous êtes à la recherche est précédée par foo et suivi par bar, et vous savez regexes ont des choses appelées assertions arrières et lookaheads, il semble donc évident que ce que vous devriez utiliser:

(?<=foo).*(?=bar) 

Prenez garde à l'évidence; très peu de regexes est intuitive. Rappelez-vous que les lookaheads ont été ajoutés assez tard aux regex, et que les lookbehinds ont été plus tardifs, mais les gens résolvaient ce genre de problème bien avant qu'ils n'arrivent. Ils l'ont fait en utilisant des groupes de capture, et qui est encore la meilleure option dans la plupart des cas:

foo(.*)bar 

Il y a aussi une erreur pure et simple dans votre regex: le ? quantificateurs sur le lookbehind:

(?<=mydomain.net)? 

Recherche EditPadPro box signale cela comme une erreur, tout comme PHP; Java et .NET non, mais je crois qu'ils devraient. Il n'a pas plus de sens que \b* ou ^+ ou ${3,7}. Ce sont toutes des assertions de largeur nulle, ce qui signifie qu'elles ne correspondent à rien, donc en ajoutant un quantificateur, vous essayez de faire correspondre le même rien plusieurs fois (rappelez-vous que $ ne correspond pas à un retour chariot, juste la position entre le caractère précédent).

Il n'y a aucun danger d'être coincé dans une boucle infinie, mais il est une bonne indication que l'auteur regex a fait une faute de frappe ou a mal compris quelque chose. Cela est particulièrement vrai lorsque le quantificateur est celui qui peut correspondre à zéro fois, comme ? ou *. Cela rend l'assertion optionnelle, et une assertion optionnelle est une assertion non pertinente. Dans votre regex, (?<=mydomain.net)? signifie "soit la position actuelle est précédée par mydomain.net ou ce n'est pas, je m'en fous de toute façon."

Quoi qu'il en soit, le Tchad est déjà venu avec une expression régulière qui fonctionne; Je voulais juste donner un aperçu de la raison pour laquelle le tien ne l'était pas. Et sur le terrain, mon anti-pattern, bien sûr. ;)

+0

Ce n'est pas tout à fait le problème que je présentais. Je cherche une chaîne précédée par "foo" et "bar" et suivie par "japh" ... dans laquelle "bar" peut ou peut ne pas être présent, mais si * est * présent, je ne veux pas capture le. –

+1

Par "foo" et "bar" je suppose que vous voulez dire 'Resource:' et le nom d'hôte, et par "japh", 'State:'; ça n'a pas d'importance. Le fait est que vous n'avez pas besoin de lookarounds pour faire correspondre ces choses; il suffit de les faire correspondre "directement" et d'utiliser le groupe de capture pour extraire la partie qui vous intéresse. Si vous n'étiez pas autorisé à utiliser un groupe de capture, vous devriez faire preuve de créativité avec des solutions de rechange, mais heureusement ce n'est pas le cas. –

1

Parfois, les choses peut être fait simple. Dans votre langue préférée, faites un split sur "myserver.net", puis faites un split sur "State:" du premier élément. par exemple en Python

>>> s="""[HQ] !!! - Alert: My Demo Website Alert Resource: demo.myserver.net Apache Web Server State: fixed""" 
>>> s.split("myserver.net")[-1].split("State:")[0] 
' Apache Web Server ' 
+0

Ah, mais je suis limité à regex - dans la longue version de la trame de fond que j'ai omis de mon post original. Désolé de ne pas être clair. –

Questions connexes