Инвертировать regexp

Sharp

Есть regexp, который позволяет выбирать нужные значения. Для конкретики, это "^aaaa:21[012][01235]$"
Как бы проще построить regexp, который выбирает значения, не попадающие в этот? Что-то у меня не получается просто взять и инвертировать его, и в голову приходят только идеи вида раскрыть этот regexp и выписать все варианты с префиксом инвертирования.

spitfire

выбирает значения, не попадающие в этот
egrep -v "regex" "file", зачем париться.

Bibi

/^(?!aaaa:21[012][01235]).{9}$/

для твоего примера можно так

Sharp

Это нужно не для grep-а, это нужно для построения фильтра, которому надо скармливать regexp

Sharp

С конструкцией ?! еще ни разу не сталкивался, нашел ее в Википедии, сейчас буду осмысливать. :)
Другой вопрос, поддерживает ли моя железка такие конструкции, это буду выяснять экспериментом.
Но в любом случае полезно знать, и, может быть, пригодиться когда буду программировать.

Bibi

вот отрывок из хорошей книжки:
Zero-width negative look-ahead assertion. The enclosed pattern must
not match for the assertion to succeed. As with the positive variant,
match text is not consumed.
This pattern can be very tricky indeed to use correctly, and reaching for
it often indicates we need to rethink the design of our search pattern.
Firstly, because the assertion is zero width, we do not absorb any match
text even if it is satisfied, so we must take that into account in the
pattern immediately following. For example, this text attempts to make
sure the last three characters of a string are not “abc”. But it doesn’t
work:
$text=~/(?!abc)$/; # ERROR: always succeeds, even if $text ends
in 'abc'
This assertion will succeed at the very end of the match text, because at
the end there are no characters left, and this does satisfy the condition.
It will still satisfy the condition even if the last three characters really
were “abc”. To have this assertion work as intended, we need to provide
a positive-width pattern for the text that isn’t “abc” to match:
$text=~/(?!abc)...$/; # OK - note the '...'
The trailing dots change the meaning of this pattern from “check the
end is not ‘abc’’’ to “look for three characters at the end which are not
‘abc’.” While these parse the same to a human, the literal-minded
regular expression engine needs the extra clarification.
Secondly, alternatives do not work as might be expected inside this
assertion. For instance, we cannot say “neither ‘a’ nor ‘b’” with
$text=~/^(?!a|b)/;
# ERROR: matches any character
This actually says “not a” or “not b”. Since “a” is not “b”, and “b” is not “a”,
anything at all will satisfy one of these conditions and the assertion will
always succeed.
Like its positive counterpart, this pattern is zero width, so it does
not affect Perl’s special regular expression variables, backreferences,
or number variables.

serega1604

>/^(?!aaaa:21[012][01235]).{9}$/
это у тебя где такой регексп работает инвертированием вышенаписанного?

Marusetta

--- тут была написана глупость, удалено ---

serega1604

ну, вот у меня например он матчит "aaaaaaaaa" но не матчит "Hello World"
есть подозрение, что .{9} задаёт жесткую длину в 9 символов.
>везде, где поддерживаются lookaround'ы
в js,grep,perl - хоть в одном из них они поддерживаются?
а то я не знаю что это такое.

Marusetta

в перле поддерживаются
признаю, я написал чушь, но как бы слово "фильтр" наводит на мысль, что на вход подаются форматные данные, и возможно, автору этого решения будет достаточно

vall

есть подозрение что в общем случае это алгоритмически неразрешимо, но для конечных языков всё точно не так печально. =)

rosali

> есть подозрение что в общем случае это алгоритмически не разрешимо
на самом деле разрешимо. регулярные выражения эквивалентны конечным автоматам, а построить отрицание конечного автомата очень просто, надо поменять терминальные и нетерминальные вершины местами.

vall

ога

Bibi

ну да, там надо ^ и $ засунуть во внутрь скобочек:
/(?!^aaaa:21[012][01235]$).{9}/

serega1604

ну и, теперь попробуй туда подставить "Hello", стало совсем чуть-чуть получше, но все равно неправильно.

Bibi

да, ты прав. все строки, короче девяти символов заваливают тест

serega1604

/?!^aaaa:21[012][01235]$).{9})|^.{1,8}$/
что-нибудь такое?

Marinavo_0507

ну построй по регулярному выражению конечный автомат, преобразуй его, и обратно переведи в регулярное выражение

doublemother

look-around'ом такое делать - бредятина и лишний геморрой. Чем извращаться с подсовыванием десятка возможных вариантов в регэксп (если хочется пудрить себе мозг look-around'ом, для такой строки придётся рассмотреть все варианты от (?!^aaaa:21[012][01235]$) до (?<!^aaaa:21[012][01235]$) ). Гораздо более разумно брать результат проверки и инвертировать, в любом языке это как бы легко реализуется, достаточно уточнить, на чём пишется фильтр:
perl:
if ($s !~ /^aaaa:21[012][01235]$/) { ... }

php:
if (!preg_match("/^aaaa:21[012][01235]$/", $s) { ... }

grep уже написали выше
и т.д.
И работать это будет намного быстрее, чем тот же look-around.

Bibi

в этом случае, да. еще хотелось бы усилить его, чтобы не опираться на знание о начале и конце строки

serega1604

а ты не можешь себе представить ситуацию, когда регексп передаётся в уже готовый метод, и даже если известны его исходники ты поимеешь кучу геммороя при обновлении.

doublemother

Могу. Но, согласись, код if (x == 0) всё-таки обычно лучше, чем if (!(x>0) && !(x<0. Регэкспы обычно занимаются проверкой на соответствие шаблону, с этим они справляются лучше, чем с проверкой на несоответствие. Поэтому лучше стараться использовать их по назначению.

Sharp

Прошу прощения, что не заглядывал в эту тему и неточностью постановки вызвал маленький срач в теме.
Если конкретней, то задача состоит в правильной реакции на bgp community, принимаемых от клиентов. У меня есть regexp по которому я выбираю community, на которые описана реакция и которые клиенты имеют право ставить, это задается изначальным regexp-ом, где заместо aaaa ставится номер моей автономной системы. Все остальные community из анонсов мне надо удалить, поэтому и пытаюсь построить некоторый инвертированный regexp.
Именно поэтому мне не подходят решения на основе grep, perl, php — мне не надо писать программу, мне надо составить regexp и впихнуть его в конфиг Juniper-а. И именно поэтому я не знаю как это правильно назвать: pcre или стандартный regexp. Конкретно, я хочу получить результат в терминах regexp-а для JunOS 9.1 И именно поэтому мне надо очень четко понять все то, что будет происходить, потому что не хочется в случае ошибки оставлять кучу народа без интернета.
Хотя, периодически я пишу разные вещи для разнообразных обработок, в том числе и на перле, поэтому отдельное спасибо за новые для меня данные про способы построения регулярных выражений. Да и кругозор просто надо расширять.
В итоге, спасибо за советы и за новые для меня знания, отдельное спасибо, что правильно восприняли мой изначальный вопрос. Но, похоже, сейчас буду придумывать какие-нить другие обходные моменты.
Хотя, может кто знает, как большие дядьки типа Level3 или ReTN решают данные проблемы?

Marinavo_0507

На цысках (и зебрах) это делается роутмапами, где есть отрицание.
Вряд ли на жуниперах такого нет, это было бы странно.
Оставить комментарий
Имя или ник:
Комментарий: