Регулярное выражение:Сопоставление методом исключения, без упреждения – возможно ли это?

StackOverflow https://stackoverflow.com/questions/466053

  •  19-08-2019
  •  | 
  •  

Вопрос

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

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

^((?!foo).)*$

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

Это было полезно?

Решение

ОБНОВЛЯТЬ: Это терпит неудачу «с двумя ff до oo», поскольку @Ciantic отметил в комментариях.


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

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

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

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

  • перл

    #!/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)
    
  • С++

    #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

Другие советы

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

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

\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

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

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

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

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

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

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

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

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

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

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

Моя первая реакция на эту ситуацию: Например, " каждая строка, в которой нет " foo " для этого " было просто использовать опцию -v инвертированный смысл соответствия в grep.

grep -v foo

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top