不能用 PCRE 正则表达式描述的内容


What can NOT be described by a PCRE regex?

我使用了很多正则表达式,并偶然发现了实际上不能用正则表达式描述的问题。

我想到的第一个例子是匹配像XOOXXXOOOOXXXXX...这样的字符串。这将是一个由 XO 的交替序列组成的字符串,其中每个仅由字符组成的子部分XO比另一个字符的先前序列长。

谁能解释一下正则表达式的正式限制是什么?我知道这可能是一个相当学术的问题,但我是一个好奇的人;-(

编辑由于我是一个php人,我对PCRE标准描述的正则表达式特别感兴趣,如下所述:http://php.net/manual/en/reference.pcre.pattern.syntax.php我知道 PCRE 允许很多不属于原始正则表达式的东西,例如反向引用。

平衡括号的数学运算似乎是一般正则表达式无法匹配的一个例子,但可以使用 PCRE 进行匹配(有关实时示例,请参阅 http://sandbox.onlinephpfunctions.com/code/fd12b580bb9ad7a19e226219d5146322a41c6e47(:

$data = array('()', '(())', ')(', '(((()', '(((((((((())))))))))', '()()');    
$regex = '/^((?:[^()]|'((?1)'))*+)$/';
foreach($data as $d) {
  echo "$d matched by regex: " . (preg_match($regex, $d) ? 'yes' : 'no') . "'n";
}

我想到的第一个例子是匹配像XOOXXXOOOOXXXXX...这样的字符串。这将是一个由 XO 的交替序列组成的字符串,其中仅由字符 XO 组成的每个子部分都比另一个字符的先前序列长。

是的,这是可以做到的。


  1. 为了匹配 x 的非空序列,后跟更多数量的 o,我们可以使用类似于平衡括号正则表达式的方法:

    (x(?1)?o)o+
    
  2. 为了匹配 x 和 o 的字符串,使得任何 x 序列后面跟一个更长的 o 序列(除了可选的最后(,我们可以在模式 #1 的基础上构建:

    ^o*(?:(x(?1)?o)o+)*x*$
    
  3. 当然,我们还需要模式 #2 的变体,其中 x 和 o 翻转:

    ^x*(?:(o(?1)?x)x+)*o*$
    
  4. 为了匹配满足上述两个条件的 x 和 o 字符串,我们可以将模式 #2 转换为积极的前瞻断言,并在模式 #3 中对捕获组重新编号:

    ^(?=o*(?:(x(?1)?o)o+)*x*$)x*(?:(o(?2)?x)x+)*o*$
    

至于主要问题。我相信 PCRE 可以匹配任何上下文无关的语言,因为对第 n个捕获组之外(?n)的支持意味着您基本上可以为每个非终端创建一个子例程。例如,这个上下文无关的语法:

  • SaTb
  • S → ε
  • TcSd
  • TeTf

可以写成:

  • 捕获组 #1 (S( → (a(?2)b|)
  • 捕获组 #2 (T( → (c(?1)d|e(?2)f)

为了将其组装成一个正则表达式,我们可以将它们全部连接起来,但在除开始非终端之外的所有内容之后附加{0},然后添加^$

^(a(?2)b|)(c(?1)d|e(?2)f){0}$

但正如你从第一个例子中看到的那样,PCRE 也可以匹配一些非上下文无关的语言。(另一个例子是 nb n cn,它是非上下文无关语言的典型示例。您可以通过使用前瞻性前瞻断言将 n b n c m 的 PCRE 与表示 mb n cn 的 PCRE 组合在一起,将其与 PCRE 匹配。尽管两种常规语言的交集必然是规则的,但两种上下文无关语言的交集不一定是上下文无关的;但是由两个 PCRE 定义的语言的交集可以由 PCRE 定义。

则表达式可以识别的所有语言的集合被称为"常规语言"。

下一个最复杂的语言是上下文无关语言。 它们不能被任何正则表达式解析。 标准示例是"所有平衡括号" - 所以"(((("和"(((("而不是"((("。

上下文无关语言的另一个很好的例子是 HTML。

我没有明确的证据表明,对于递归、平衡组、自引用组以及将文本附加到正在测试的字符串,这些是不可能的。我很高兴在任何或所有这些方面被证明是错误的,因为我会学到一些东西!

  1. 数学很差。

例如,我不认为使用 PCRE 可以检测升序的数字序列:即匹配"1 2 7 97 315 316..."而不是"127 97 315 316...">

  1. 我不确定是否有可能匹配从 1 连续增加的序列,例如"1 2 3...",而无需详尽列出所有可能性,例如/1( 2( 3(...)?)?)?/直到您希望检查的最大长度。

这些黑客通过将已知文本添加到被测试的字符串来使其工作(例如 http://www.rexegg.com/regex-trick-line-numbers.html 通过在文件末尾添加一系列数字来工作(。但作为原始正则表达式,简单的数学只能通过暴力破解来实现。

  1. 另一个我认为它会失败的例子是"匹配任何总和为 N 的序列"。

所以对于 N=4,它应该匹配 43 11 32 21 1 1 12 1 11 2 11 1 21 1 1 1,看起来你可以暴力破解它,直到你意识到它也必须匹配4 -12 11 0 1

  1. 以同样的方式,我认为你不能让它使用 SI 单位分析方程,并验证单位是否在方程的两侧平衡。例如,"10N=2kg*5ms^-1"。不要介意检查值,只需检查单位是否正确。

  2. 然后是目前没有计算机程序可以完成的所有类别的问题,例如"检查字符串是否在英语中正确标题大小写",这将需要一个上下文相关的自然语言解析器来正确检测"时间像箭一样飞逝但水果像香蕉一样飞翔"中的"喜欢"的不同含义。