使用正则表达式(PCRE)匹配a ^ n b ^ n c ^ n(例如“aaabbbccc”)

时间:2011-09-15 16:28:59

标签: php regex pcre

众所周知,现代正则表达式实现(最值得注意的是PCRE)与regular grammars的原始概念几乎没有共同之处。例如,您可以解析context-free grammar {a n b n 的经典示例; n> 0}(例如aaabbb)使用此正则表达式(demo):

~^(a(?1)?b)$~

我的问题是:你能走多远?是否也可以解析context-sensitive grammar {a n b n c n ; n> 0}(例如{{1}使用PCRE?

4 个答案:

答案 0 :(得分:33)

受到NullUserExceptions答案的启发(他已经因为一个案例失败而已删除)我想我自己找到了一个解决方案:

$regex = '~^
    (?=(a(?-1)?b)c)
     a+(b(?-1)?c)
$~x';

var_dump(preg_match($regex, 'aabbcc'));    // 1
var_dump(preg_match($regex, 'aaabbbccc')); // 1
var_dump(preg_match($regex, 'aaabbbcc'));  // 0
var_dump(preg_match($regex, 'aaaccc'));    // 0
var_dump(preg_match($regex, 'aabcc'));     // 0
var_dump(preg_match($regex, 'abbcc'));     // 0

亲自尝试:http://codepad.viper-7.com/1erq9v


解释

如果你考虑没有正向前瞻断言的正则表达式((?=...)部分),你就有了这个:

~^a+(b(?-1)?c)$~

这只会检查是否存在任意数量的a s,后跟相同数量的bc s。

这还不能满足我们的语法,因为a的数量也必须相同。我们可以通过检查a的数量是否等于b的数量来确保。这就是前瞻断言中的表达式:(a(?-1)?b)cc是必需的,因此我们不仅匹配b的一部分。


结论

我认为这令人印象深刻地表明,现代正则表达式不仅能够解析非常规语法,而且甚至可以解析非上下文无关的语法。希望这将打破无休止的鹦鹉学舌“你不能用正则表达式做X,因为X不规则”

答案 1 :(得分:11)

  

我的问题是:你能走多远?

为了不创建一个难以理解的标点符号的代码,我将冒险投票并回答一个不同的,但非常相关的问题: 应该 >你去?

正则表达式解析器是您工具箱中的辉煌事物,但它们不是全部并且完成所有编程。以可读的方式编写解析器的能力 是您工具箱中的一件好事。

应该使用正则表达式直到它们开始使您的代码难以理解。除此之外,他们的价值充其量是可疑的,最糟糕的是破坏性。对于这种特殊情况,而不是使用像hideous这样的东西:

~^(?=(a(?-1)?b)c)a+(b(?-1)?c)$~x

(向NikiC道歉),绝大多数人试图维护它要么必须完全取代,要么花费大量的时间阅读和理解,你可能要考虑类似于非RE,“正确解析器”解决方案(伪代码):

# Match "aa...abb...bcc...c" where:
# - same character count for each letter; and
# - character count is one or more.

def matchABC (string str):
    # Init string index and character counts.
    index = 0
    dim count['a'..'c'] = 0

    # Process each character in turn.
    for ch in 'a'..'c':
        # Count each character in the subsequence.
        while index < len(str) and str[index] == ch:
            count[ch]++
            index++

    # Failure conditions.
    if index != len(str):        return false # did not finish string.
    if count['a'] < 1:           return false # too few a characters.
    if count['a'] != count['b']: return false # inequality a and b count.
    if count['a'] != count['c']: return false # inequality a and c count.

    # Otherwise, it was okay.
    return true

这将在未来更容易维护。我总是向人们建议他们应该假设那些追随他们的人(他们必须维护他们所写的代码)是知道你住在哪里的精神病患者 - 在我的情况下,这可能是对的,我不知道你住在哪里: - )

除非您对此类正则表达式有真正的需求(有时候有很好的理由,例如解释语言中的性能),否则应优化可读性第一

答案 2 :(得分:10)

以下是使用带有.NET正则表达式的balancing groups的替代解决方案:

^(?'a'a)+(?'b-a'b)+(?(a)(?!))(?'c-b'c)+(?(b)(?!))$

不是PCRE,但可能会引起关注。

示例:http://ideone.com/szhuE

编辑:为组a添加了缺失的平衡检查,以及在线示例。

答案 3 :(得分:2)

Qtax Trick

没有提到的解决方案:

^(?:a(?=a*(\1?+b)b*(\2?+c)))+\1\2$

the regex demo中查看匹配和失败的内容。

这使用自引用组(his vertical regex上使用的@Qtax的想法)。