【问题标题】:Fixed-length regex lookbehind complains of variable-length lookbehind固定长度的正则表达式lookbehind抱怨可变长度的lookbehind
【发布时间】:2014-11-04 15:20:45
【问题描述】:

这是我要运行的代码:

$str = 'a,b,c,d';
return preg_split('/(?<![^\\\\][\\\\]),/', $str);

如您所见,这里使用的正则表达式是:

/(?<![^\\][\\]),/

这是一个简单的固定长度的负向回溯,用于“前面是不是反斜杠的东西,然后是反斜杠!”。

这个正则表达式在http://www.phpliveregex.com上工作得很好

但是当我去实际尝试运行上述代码时,我会吐回错误:

Warning:  preg_split() [function.preg-split]: Compilation failed: lookbehind assertion is not fixed length at offset 13

更糟糕的是,一位程序员同事在他的 5.4.24 PHP 服务器上测试了代码,并且运行良好。

这让我相信我的问题与我几乎无法控制的服务器配置有关。我被告知我的 PHP 版本是 5.2.*

对于 preg_replace() 是否有任何可能不存在此问题的解决方法/替代方法?

【问题讨论】:

  • 当运行代码确实可以正常工作并拆分结果。
  • 可能不是真正的 5.2。它对我有用,您可以查看它在哪些版本中失败here(在 4.3.0 - 4.4.4、5.0.0 - 5.1.6 中失败)。请注意,您运行的是 ancient PHP 版本。
  • 您可以使用'/(?&lt;!(?&lt;!\\\\)\\\\),/',在所有版本中都成功(注意:修复了此评论之前版本中的逻辑错误)。
  • 但是,这是否会按预期拆分字符串a\\\,b? (或:'a\\\\\\,b' 带转义引号)。
  • FWIW:我更喜欢匹配而不是拆分以获得独立于其中 \ 数量的可靠行为:preg_match_all('/(?:[^\\\\,]|\\\\.)+/u',$str,$matches);(旧的 '不是转义符或分隔符,或直接转义后面跟着你喜欢的任何东西 - 序列')

标签: php regex


【解决方案1】:

避免否定字符类的方法(为了更清楚,我写了\x5c而不是很多反斜杠)

$result = preg_split('/(?<!(?!\x5c).\x5c),/s', $str);

关于方法本身:

如果您尝试在未转义的逗号上进行拆分,则您的后视方式是错误的,因为您无法检查逗号前的反斜杠数量和未定义的数量。你有几种可能解决这个问题:

$result = preg_split('/(?:[^\x5c]|\A)(?:\x5c.)*\K,/s', $str);

$result = preg_split('/(?<!\x5c)(?:\x5c.)*\K,/s', $str);

或对于 PHP > 5.2.4

$result = preg_split('/\x5c{2}(*SKIP)(?!)|(?<!\x5c),/s', $str);

【讨论】:

    【解决方案2】:

    此问题是由 PCRE 6.7 中修复的错误引起的。引用the changelog:

    否定的单字符类未被识别为 诸如(?&lt;=[^f]) 等后向断言中的固定长度,导致 不正确的编译错误"lookbehind assertion is not fixed length"

    PCRE 6.7 于 2006 年 11 月在 PHP 5.2.0 中引入。由于您仍然有这个错误,这意味着它不在您的服务器上 - 所以对于基于 preg-split 的解决方法,您必须使用没有否定的模式字符类。例如:

    $patt = '/(?<!(?<!\\\\)\\\\),/';
    // or...
    $patt = '/(?<![\x00-\x5b\x5d-\xFF]\x5c),/';
    

    但是,我发现整个方法有点奇怪:如果 , 符号前面正好有三个反斜杠怎么办?还是五个?还是其中的奇数个?这种情况下的逗号应被视为“转义”,但显然您不能创建可变长度的后向表达式来涵盖这些情况。

    再想一想,可以改用preg_match_all,用一种常见的交替技巧来覆盖转义符号:

    $str = 'e ,a\\,b\\\\,c\\\\\\,d\\\\';
    preg_match_all('/(?:[^\\\\,]|\\\\(?:.|$))+/', $str, $matches);
    var_dump($matches[0]);
    

    Demo

    我真的认为我在这里涵盖了所有问题,那些尾部的斜线是一个杀手)

    【讨论】:

    • 非常感谢!一个很好的组合,内容丰富的答案。正是 SO 需要的那种内容:)
    【解决方案3】:

    我认为您使用的是较旧的 php 版本,因为您的错误出现在 PHP 5.1.6 或更低版本。

    您可以查看non working demo here

    另一方面,它适用于 PHP 5.2.16 或更高版本:

    Working demo

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-23
      • 2017-11-30
      • 1970-01-01
      • 2018-05-29
      • 2015-10-12
      • 2022-09-23
      相关资源
      最近更新 更多