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

众所周知的事实是,现代正则表达式实现(最著名的是PCRE)与正则语法的原始概念几乎没有共通之处。 例如,您可以解析上下文无关语法的经典示例{anbn; n> 0}(例如aaabbbccc)使用此正则表达式(演示):

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

我的问题是:您能走多远? 是否还可以使用PCRE来解析上下文相关文法{anbncn; n> 0}(例如aaabbbccc)?

NikiC asked 2020-08-10T17:20:46Z
4个解决方案
33 votes

受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部分),则具有以下内容:

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

这无非就是检查是否有任意数量的as,然后是相等数量的bs和as。

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


结论

我认为这令人印象深刻,表明现代正则表达式不仅能够解析非常规语法,而且甚至可以解析非上下文无关的语法。 希望这将解决“您不能使用正则表达式来执行X,因为X不规则”的无休止的模仿。

NikiC answered 2020-08-10T17:21:23Z
11 votes

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

为了不创建标点符号难以理解的代码,我将冒风险投票的风险,并回答一个与问题非常相关的不同问题:您应该走多远?

正则表达式解析器在您的工具包中是一个绝妙的事情,但是它们并不是编程的全部内容。 以可读的方式编写解析器的能力在您的工具包中也是一件绝妙的事情。

正则表达式应一直使用到它们开始使您的代码难以理解为止。 除此之外,它们的价值充其量是可疑的,最坏的是其损坏。 对于这种特定情况,不要使用类似丑陋的东西:

~^(?=(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

将来将更容易维护。 我总是喜欢向人们建议,他们应该假设那些紧随其后的人(必须维护他们编写的代码)是精神病患者,他们知道您的住所-就我而言,可能一半正确,我不知道您的住所 :-)

除非您确实需要这种正则表达式(有时有充分的理由,例如解释语言的性能),否则应首先针对可读性进行优化。

paxdiablo answered 2020-08-10T17:22:12Z
10 votes

这是使用带有.NET正则表达式的平衡组的替代解决方案:

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

不是PCRE,但可能很有趣。

例如:[http://ideone.com/szhuE]

编辑:添加了缺少的平衡检查组a和一个在线示例。

Qtax answered 2020-08-10T17:22:44Z
2 votes

Qtax技巧

未提及的解决方案:

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

在正则表达式演示中查看哪些匹配项和失败项。

这使用了自引用组(在他的垂直正则表达式中使用了一个@Qtax想法)。

zx81 answered 2020-08-10T17:23:17Z
translate from https://stackoverflow.com:/questions/7434272/match-an-bn-cn-e-g-aaabbbccc-using-regular-expressions-pcre