PHP Regex,忽略Alternating语句中的第一个分组

时间:2011-03-16 23:03:29

标签: php regex

我试图找出如果使用preg_match不存在另一个语句时如何捕获一个语句。

示例文字:

<!-- InstanceBeginEditable name="doctitle" -->

<title>BU Libraries | Research Guides | Citing Your Sources</title>

<!-- InstanceEndEditable -->

<div id="standardpgt"><h1><!-- InstanceBeginEditable name="pagetitle" --><strong>Citing Your Sources</strong><!-- InstanceEndEditable --></h1></div>

因为存在pagetitle我想拉取它而不是doctitle标签。当然,它们之间还有很多其他角色,但我想向你展示一些小样本。

如果pagetitle不存在,我想获取doctitle的内容。

扭曲的是我没有直接使用php代码,我通过配置文件传递一个正则表达式语句,然后脚本正在接受它并从语句中拉出第一组。

这就是我提出的:

((?!.*?<!--\s*?InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->.*?<!--\s*?InstanceEndEditable\s*?-->)<!--\s*?InstanceBeginEditable\s*?name=\x22doctitle\x22\s*?-->\s*?<title>(.*?)<\/title>\s*?<!--\s*?InstanceEndEditable\s*?-->|<!-- InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->(.*?)<!--\s*?InstanceEndEditable\s*?-->)

问题是由于某种原因,如果它不起作用,php总是将第一个空组读取为组1。

例如,在上面的示例文本中,它将返回

0 -> <!-- InstanceBeginEditable name="pagetitle" --><strong>Citing Your Sources</strong><!-- InstanceEndEditable -->
1 -> 
2 -> <strong>Citing Your Sources</strong>

我不能为生活弄清楚如何使这项工作。我也写了这个正则表达式:

(?(?=.*?<!--\s*?InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->.*?<!--\s*?InstanceEndEditable\s*?-->).*?<!-- InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->(.*?)<!--\s*?InstanceEndEditable\s*?-->|.*?<!--\s*?InstanceBeginEditable\s*?name=\x22doctitle\x22\s*?-->\s*?<title>(.*?)<\/title>\s*?<!--\s*?InstanceEndEditable\s*?-->)

但这也不起作用。非常感谢您的帮助。

克里斯

2 个答案:

答案 0 :(得分:6)

只需在整个表达式周围使用分支重置模式:(?| ...),如:

((?|(?!.*?<!--\s*?InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->.*?<!--\s*?InstanceEndEditable\s*?-->)<!--\s*?InstanceBeginEditable\s*?name=\x22doctitle\x22\s*?-->\s*?<title>(.*?)<\/title>\s*?<!--\s*?InstanceEndEditable\s*?-->|<!-- InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->(.*?)<!--\s*?InstanceEndEditable\s*?-->))s

来自“man perlre”:

  

“(|图案)”   这是“分支重置”模式,具有特殊性   捕获缓冲区的属性   编号从相同的起点开始   在每个交替分支。它可以从   perl 5.10.0。

     

捕获缓冲区从左到右编号,但是   在这个构造里面编号是   为每个分支重新启动。

     

每个分支内的编号将正常,任何   遵循此构造的缓冲区将   编号好像构造一样   只包含一个分支,即包含分支的分支   大多数捕获缓冲区。

     

当您想要捕获一个时,此构造将非常有用   许多替代比赛。

     

考虑以下模式。数字   下面显示哪个缓冲区   捕获的内容将被存储。

         # before  ---------------branch-reset----------- after
         / ( a )  (?| x ( y ) z | (p (q) r) | (t) u (v) ) ( z ) /x
         # 1            2         2  3        2     3     4

答案 1 :(得分:1)

user178551在推荐使用分支重置构造时绝对正确。您的原始正则表达式基本上没有任何问题(除了它超过300个字符并且 ALL ON ONE LINE! - 并且它无法将两个替代品中的一个放入单一捕获组)。像这样的一个非平凡的(温和的)正则表达式需要用自由间距模式写入缩进,这样你才能真正读取它。这是你的原始正则表达式,添加了一些合理的空格:

$re_OP1 = '%
    (                                             # $1:
      (?!
        .*?<!--\s*?InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->
        .*?<!--\s*?InstanceEndEditable\s*?-->
      )
           <!--\s*?InstanceBeginEditable\s*?name=\x22doctitle\x22\s*?-->\s*?
           <title>(.*?)<\/title>\s*?              # $2: 
           <!--\s*?InstanceEndEditable\s*?-->
    |      <!-- InstanceBeginEditable\s*?name=\x22pagetitle\x22\s*?-->
           (.*?)                                  # $3;
           <!--\s*?InstanceEndEditable\s*?-->
    )
    %six';

现在看一下这个正则表达式,你可以看到你在OR运算符的行上硬编码了一个空格(即|<!-- InstanceBegin...)。这将导致正则表达式无法与应用的'x'修饰符匹配。所以用\s*替换这个空间并在测试数据上运行它,这是我得到的结果(php-5.2.14):

Array
(
    [0] => <!-- InstanceBeginEditable name="pagetitle" --><strong>Citing Your Sources</strong><!-- InstanceEndEditable -->
    [1] => <!-- InstanceBeginEditable name="pagetitle" --><strong>Citing Your Sources</strong><!-- InstanceEndEditable -->
    [2] =>
    [3] => <strong>Citing Your Sources</strong>
)

这些结果与您发布的结果类似(但由于某种原因,您的结果只显示了2个捕获组???)我们现在需要做的就是应用user178551的分支重置建议,正则表达式解决方案变为:

$re_jmr = '%
    (?|  # Branch reset construct. (restart counting for each alternative)
      (?!
        .*?<!--\s*InstanceBeginEditable\s*name="pagetitle"\s*-->
        .*?<!--\s*InstanceEndEditable\s*-->
      )
           <!--\s*InstanceBeginEditable\s*name="doctitle"\s*-->\s*
           <title>(.*?)<\/title>\s*              # $1: Group 1A
           <!--\s*InstanceEndEditable\s*-->
    |      <!--\s*InstanceBeginEditable\s*name="pagetitle"\s*-->
           (.*?)                                  # $1: Group 1B
           <!--\s*InstanceEndEditable\s*-->
    )
    %six';

我已经将所有懒惰的\s*?变为贪婪(因为贪婪是你想要的)。我还将所有\x22更改为" - 更短且更易读的恕我直言。以下是运行这个新的分支重置正则表达式的结果:

Array
(
    [0] => <!-- InstanceBeginEditable name="pagetitle" --><strong>Citing Your Sources</strong><!-- InstanceEndEditable -->
    [1] => <strong>Citing Your Sources</strong>
)

这是(如果我没有记错的话),正是你要找的。 (你没有提供其他替代方案的测试用例,因此尚未经过测试。)除此之外,你的原始正则表达式非常接近。