Regex: Сопоставление по исключению, без прогнозирования - возможно ли это? - PullRequest
27 голосов
/ 21 января 2009

В некоторых разновидностях регулярных выражений [отрицательные] утверждения нулевой ширины (упреждающий просмотр / обратный просмотр) не поддерживаются.

Это делает чрезвычайно трудным (невозможным?) Утверждение исключения. Например, «каждая строка, которая не имеет« foo »на ней», например:

^((?!foo).)*$

Может ли то же самое быть достигнуто без использования осмотра вообще (на данный момент проблемы сложности и производительности оставлены без внимания)?

Ответы [ 4 ]

30 голосов
/ 21 января 2009

ОБНОВЛЕНИЕ: Сбой "с двумя ff перед oo", как указано @ Ciantic в комментариях.


^(f(o[^o]|[^o])|[^f])*$

ПРИМЕЧАНИЕ: Гораздо проще просто отменить совпадение на стороне клиента, чем использовать приведенное выше регулярное выражение.

Регулярное выражение предполагает, что каждая строка заканчивается символом новой строки, если это не так, то смотрите регулярные выражения C ++ и grep.

Примеры программ на Perl, Python, C ++ и grep все дают одинаковый вывод.

  • * Perl 1021 *

    #!/usr/bin/perl -wn
    print if /^(f(o[^o]|[^o])|[^f])*$/;
    
  • питон

    #!/usr/bin/env python
    import fileinput, re, sys
    from itertools import ifilter
    
    re_not_foo = re.compile(r"^(f(o[^o]|[^o])|[^f])*$")
    for line in ifilter(re_not_foo.match, fileinput.input()):
        sys.stdout.write(line)
    
  • C ++

    #include <iostream>
    #include <string>
    #include <boost/regex.hpp>
    
    int main()
    {
      boost::regex re("^(f(o([^o]|$)|([^o]|$))|[^f])*$");
      //NOTE: "|$"s are there due to `getline()` strips newline char
    
      std::string line;
      while (std::getline(std::cin, line)) 
        if (boost::regex_match(line, re))
          std::cout << line << std::endl;
    }
    
  • Grep

    $ grep "^\(f\(o\([^o]\|$\)\|\([^o]\|$\)\)\|[^f]\)*$" in.txt
    

Пример файла:

foo
'foo'
abdfoode
abdfode
abdfde
abcde
f

fo
foo
fooo
ofooa
ofo
ofoo

Выход:

abdfode
abdfde
abcde
f

fo
ofo
4 голосов
/ 10 мая 2018

Наткнулся на этот вопрос и принял тот факт, что не было полностью работающего регулярного выражения, как личный вызов. Я считаю, что мне удалось создать регулярное выражение, которое работает для всех входов - при условии, что вы можете использовать атомная группировка / собственнические квантификаторы .

Конечно, я не уверен, есть ли какие-либо ароматы, которые позволяют атомарную группировку, но не обходные, но Вопрос задал вопрос, возможно ли в регулярном выражении указать исключение без обходного пути, и это технически возможно :

\A(?:$|[^f]++|f++(?:[^o]|$)|(?:f++o)*+(?:[^o]|$))*\Z

Пояснение:

\A                         #Start of string
(?:                        #Non-capturing group
    $                      #Consume end-of-line. We're not in foo-mode.
    |[^f]++                #Consume every non-'f'. We're not in foo-mode.
    |f++(?:[^o]|$)          #Enter foo-mode with an 'f'. Consume all 'f's, but only exit foo-mode if 'o' is not the next character. Thus, 'f' is valid but 'fo' is invalid.
    |(?:f++o)*+(?:[^o]|$)  #Enter foo-mode with an 'f'. Consume all 'f's, followed by a single 'o'. Repeat, since '(f+o)*' by itself cannot contain 'foo'. Only exit foo-mode if 'o' is not the next character following (f+o). Thus, 'fo' is valid but 'foo' is invalid.
)*                         #Repeat the non-capturing group
\Z                         #End of string. Note that this regex only works in flavours that can match $\Z

Если по какой-либо причине вы можете использовать атомарную группировку, но не собственнические квантификаторы или обходные пути, вы можете использовать:

\A(?:$|(?>[^f]+)|(?>f+)(?:[^o]|$)|(?>(?:(?>f+)o)*)(?:[^o]|$))*\Z

Однако, как отмечают другие, возможно, более практично просто отрицать совпадение другими способами.

1 голос
/ 06 августа 2009

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

Моя первоначальная реакция на эту ситуацию: Например, «каждая строка, в которой нет« foo »» просто использовала опцию -v invert sense of match в grep.

grep -v foo

возвращает все строки в файле, которые не соответствуют 'foo'

Это так просто, у меня сильное чувство, что я просто неправильно понял твой вопрос ...

1 голос
/ 21 января 2009

Обычно вы можете искать foo и инвертировать результат соответствия регулярному выражению из кода клиента.

Для простого примера, допустим, вы хотите проверить, что строка содержит только определенные символы.

Вы могли бы написать это так:

^[A-Za-z0-9.$-]*$

и примите результат true как действительный или такой:

[^A-Za-z0-9.$-]

и принять false результат действительным.

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

...