Проблема с проверкой утверждения и необязательной подстроки - PullRequest
0 голосов
/ 16 февраля 2010

Я пытаюсь написать некоторое регулярное выражение, которое будет анализировать информацию из предупреждений, генерируемых Hyperic HQ. Уведомления приходят в виде электронных писем с темой, например:

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

Короче говоря, мне нужно иметь возможность последовательно получать часть "Apache Web Server", независимо от имени хоста, которое может даже не присутствовать. Я знаю, что имя хоста всегда будет заканчиваться на «myserver.net».

Регулярное выражение, которое у меня есть:

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

Я ожидал, что это будет соответствовать нулю или более символов между "Resource:" и "State:", опционально следуя (но не включая) имени хоста.

К сожалению, он возвращает "Server", то есть последнее слово в бите, которое я хочу сопоставить. Это происходит независимо от того, находится ли имя хоста в строке.

Может кто-нибудь помочь?

РЕДАКТИРОВАТЬ: Решение, предоставленное Чадом ниже

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

Ответы [ 3 ]

3 голосов
/ 17 февраля 2010

Это пример антипаттерна, который я называю Преждевременное обращение к внешнему виду . Вы знаете, что искомой строке предшествует foo, за которой следует bar, и вы знаете, что в регулярных выражениях есть вещи, называемые lookbehinds и lookaheads, поэтому очевидно, что вам следует использовать:

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

Остерегайся очевидного; очень мало о регулярных выражениях интуитивно понятно. Помните, что предпросмотр был довольно поздним дополнением к регулярным выражениям, а взгляды запаздывали даже позже, но люди решали эту проблему задолго до того, как пришли. Они сделали это, используя группы захвата, и это по-прежнему лучший вариант в большинстве случаев:

foo(.*)bar

В вашем регулярном выражении также есть прямая ошибка: квантификатор ? на вид сзади:

(?<=mydomain.net)?

Поле поиска EditPadPro помечает это как ошибку, как и PHP; Java и .NET нет, но я считаю, что они должны. Это не имеет больше смысла, чем \b* или ^+ или ${3,7}. Все это утверждения нулевой ширины, что означает, что они ничего не соответствуют, поэтому, добавляя квантификатор, вы пытаетесь сопоставить одно и то же ничто несколько раз (помните, что $ не соответствует символу новой строки, просто позиция между перевод строки и предыдущий символ).

Нет опасности застрять в бесконечном цикле, но это хороший признак того, что автор регулярного выражения сделал опечатку или неправильно что-то понял. Это особенно верно, когда квантификатор равен единице, которая может совпадать с нулем раз, например ? или * Это делает утверждение необязательным, а необязательное утверждение является несоответствующим утверждением. В вашем регулярном выражении (?<=mydomain.net)? означает «либо текущей позиции предшествует mydomain.net, либо нет; мне все равно».

Во всяком случае, Чад уже придумал регулярное выражение, которое работает; Я просто хотел дать некоторое представление о том, почему ваши нет. И полевые испытания моего анти-паттерна, конечно. ;)

2 голосов
/ 17 февраля 2010

Это похоже на те тесты, которые я написал

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

Это будет в именованной группе захвата "PartIWant", если ваш механизм регулярных выражений поддерживает именованные группы захвата.

EDIT: Я протестировал это регулярное выражение с обеими этими строками

[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
1 голос
/ 17 февраля 2010

Иногда все можно сделать просто. На вашем любимом языке выполните разделение на «myserver.net», а затем разделите «State:» первого элемента. например, в 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 '
...