30

在某些正则表达式风格中,不支持 [负] 零宽度断言(前瞻/后瞻)。

这使得声明排除非常困难(不可能?)。例如“每行没有“foo”的行”,像这样:

^((?!foo).)*$

完全不使用环视能否实现相同的目标(暂时搁置复杂性和性能问题)?

4

4 回答 4

30

更新:正如@Ciantic 在评论中指出的那样,它“在oo 之前有两个ff”失败了。


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

注意:在客户端否定匹配比使用上面的正则表达式要容易得多。

正则表达式假定每行都以换行符结尾,如果不是,请查看 C++ 和 grep 的正则表达式。

Perl、Python、C++ 和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)
    
  • 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
于 2009-01-21T17:16:53.597 回答
5

遇到了这个问题,并将没有一个完全正常工作的正则表达式作为个人挑战。我相信我已经设法创建了一个适用于所有输入的正则表达式-只要您可以使用原子分组/占有量词

当然,我不确定是否有任何风格允许原子分组但不允许环视,但问题询问是否可以在正则表达式中声明不环视的排除,这技术上是可能的:

\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

但是,正如其他人指出的那样,通过其他方式否定匹配可能更实际。

于 2018-05-10T15:18:58.123 回答
2

我偶然发现了这个问题,寻找我自己的正则表达式排除解决方案,我试图我的正则表达式中排除一个序列。

我对这种情况的最初反应: 例如“每行没有“foo”的行” 只是在 grep 中使用 -v 反转匹配选项。

grep -v foo

这将返回文件中与 'foo' 不匹配的所有行

这很简单,我有一种强烈的感觉,我只是误读了你的问题......

于 2009-08-06T17:05:46.087 回答
1

您通常可以查找 foo 并从客户端代码中反转正则表达式匹配的结果。

举个简单的例子,假设您想验证一个字符串是否只包含某些字符。

你可以这样写:

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

并接受一个true有效的结果,或者像这样:

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

并接受一个false有效的结果。

当然,这并不总是一种选择:例如,有时您只需将表达式放在配置文件中或将其传递给另一个程序。但值得记住。您的具体问题,例如,如果您可以像这样使用否定,则表达式会简单得多

于 2009-01-21T16:57:34.837 回答