Regex 正则表达式:通过排除进行匹配,而不进行前瞻-这可能吗?

Regex 正则表达式:通过排除进行匹配,而不进行前瞻-这可能吗?,regex,regex-negation,Regex,Regex Negation,在某些正则表达式风格中,不支持[负]零宽度断言(向前看/向后看) 这使得声明排除非常困难(不可能?)。例如“每一行上没有“foo”的内容”,如下所示: ^((?!foo).)*$ 同样的事情是否可以在不使用look-around的情况下实现(暂时不考虑复杂性和性能问题)?您通常可以查找foo并从客户机代码中反转正则表达式匹配的结果 举个简单的例子,假设您想验证字符串是否只包含某些字符 你可以这样写: ^((?!foo).)*$ ^[A-Za-z0-9.$-]*$ 并将true结果视为有效,或

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

这使得声明排除非常困难(不可能?)。例如“每一行上没有“foo”的内容”,如下所示:

^((?!foo).)*$

同样的事情是否可以在不使用look-around的情况下实现(暂时不考虑复杂性和性能问题)?

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

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

你可以这样写:

^((?!foo).)*$
^[A-Za-z0-9.$-]*$

并将
true
结果视为有效,或类似以下情况:

^((?!foo).)*$
[^A-Za-z0-9.$-]

并将
false
结果视为有效

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

更新:它会失败,因为“oo之前有两个ff”


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

正则表达式假设每一行都以换行符结尾,如果不是,请参阅C++和grep的正则表达式

Perl、Python、C++、GRP中的示例程序都给出相同的输出。

  • 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;
    }
    
示例文件:

foo
'foo'
abdfoode
abdfode
abdfde
abcde
f

fo
foo
fooo
ofooa
ofo
ofoo
输出:

abdfode
abdfde
abcde
f

fo
ofo

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

我对这种情况的最初反应是:例如,“每一行上没有“foo”的内容”只是在grep中使用-v inverse-sense-of-matching选项

grep -v foo
这将返回文件中与“foo”不匹配的所有行


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

遇到了这个问题,并将没有一个完全正常工作的正则表达式这一事实视为个人的挑战。我相信我已经成功地创建了一个正则表达式,它对所有输入都有效——只要您可以使用/

当然,我不确定是否有允许原子分组但不允许查找的味道,但问题是,在regex中,是否有可能在不允许查找的情况下声明排除,并且在技术上是可能的:

\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

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

我知道后处理可以解决问题。。。这正是我试图避免的,我在寻找一款香草正则表达式,它能做正确的事情。另外,我正在寻找一种不允许特定字符序列,而不是无序集的方法。很明显,在程序中进行后处理时,否定匹配是首选方法。有时你没有选择,即使你有选择,知道你的选择也是好的。这个正则表达式是不正确的。它与
f
fo
barf
不匹配。但是这个是:
^(f(o([^o]|$)[o]|$)[124;[^f])*$
@J.f.塞巴斯蒂安:啊,你说得对。我想知道他为什么不改变其他的。答案似乎不适用于
somethingffoosomething
,在oo之前有两个ff。答案不错,但是
foo
有两个相似的字符这一事实并不能使问答变得通用。最好是用abc
grep-vfoo
搜索“foo”并否定结果,OP说他希望regex自己来做这项工作。但是假设需求是“包含'foo'而不包含'bar'”,并且您只能执行一个正则表达式匹配?那么简单地否定结果就不是一个选项了。@Alan:是的,但是为什么(看起来)任意的一个regexp匹配限制呢?如果我们不局限于一个匹配,那么我们可以通过管道:
grep foo | grep-v bar
。我之所以提出这个问题,是因为我无法理解上面的示例并使它们在Emacs中工作,但我能够在命令行上完成这项工作。@ZacharyYoung:当然
grep-v
或等效的方法是最好的方法,如果可以的话。但是OP谈论的是一种假设的情况,你不能反转比赛,也不能使用lookaheads。幸运的是,这种情况在现实世界中极为罕见@你说阿兰摩尔非常罕见?这里是,想想这个问题已经问了7年了。点击“regex否定”标签可以看到一些类似的问题。@finnw:很好,谢谢。实际上可以表达
^(?。*foo)
而不需要任何扩展的regex功能:D本例中的解决方案是:
^([^f]|(f+o)*f+([^fo o]| o([^fo o]|$)*
。我们甚至可以非常优雅地将其扩展到任意子字符串“foo”。。。我很快就会发布一篇详细的评论!双“oo”使阅读这些变得无限复杂。理解这一点的人能否创建一个不包含该内容的内容。@Preston您希望非环视正则表达式查找(比如)不包含“sna”的行<代码>\A(?:$|[^s]+++s++(?:[^n]|$)|(?:s++n)*++(?:[^A]|$)*\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