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

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

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

^((?!foo).)*$

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

Ответ 1

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


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

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

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

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

  • perl

    #!/usr/bin/perl -wn
    print if /^(f(o[^o]|[^o])|[^f])*$/;
    
  • python

    #!/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

Ответ 2

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

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

Вы можете написать так:

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

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

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

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

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

Ответ 3

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

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

grep -v foo

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

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