正则表达式的奇怪问题

时间:2012-08-30 16:01:52

标签: php regex preg-match

我有一个正则表达式:

~(?P<opening>{(?P<inverse>[!])?block:(?P<name>[a-z0-9\s_-]+)(:(?P<function>[a-z0-9\s_-]+)([\s]?\((?P<params>[^)]*)\))?)?})(?P<contents>[^{]*(?:\{(?!/?block:[a-z0-9\s_-]+\})[^{]*)*)(?P<closing>{/block:(?P=name)})~is

尝试匹配以下内容:

<ul>{block:menu}
    <li><a href="{var:link}">{var:title}</a>
{/block:menu}</ul>

哪个工作正常,但是当引入块标记的第3部分时,例如:{block:menu:thirdbit}它无法匹配它,但是如果你砍掉正则表达式的末尾以将其修剪为下面的它 匹配意味着该模式没问题,但其他方面出错:

(?P<opening>{(?P<inverse>[!])?block:(?P<name>[a-z0-9\s_-]+)(:(?P<function>[a-z0-9\s_-]+)([\s]?\((?P<params>[^)]*)\))?)?})

任何想法出了什么问题?

2 个答案:

答案 0 :(得分:1)

只是一个想法:将所有{block:menu}和类似元素转换为自己命名空间中的XML元素。然后,您可以使用xpath并完成作业。你甚至可以在飞行中做到这一点。

答案 1 :(得分:1)

正如Tim正确指出的那样 - 用正则表达式解析HTML是不明智的。

第二:如上所述,问题中的正则表达式不可读。我冒昧地重新格式化它。这是一个工作脚本,其中包含完全相同的正则表达式的注释可读版本:

<?php // test.php Rev:20120830_1300
$re = '%
    # Match a non-nested "{block:name:func(params)}...{/block:name}" structure.
    (?P<opening>                      # $1: == $opening: BLOCK start tag.
      {                               # BLOCK tag opening literal "{"
      (?P<inverse>[!])?               # $2: == $inverse: Optional "!" negation.
      block:                          # Opening BLOCK tag ident.
      (?P<name>[a-z0-9\s_-]+)         # $3: == $name: BLOCK element name.
      (                               # $4: Optional BLOCK function.
        :                             # Function name preceded with ":".
        (?P<function>[a-z0-9\s_-]+)   # $function: Function name.
        (                             # $5: Optional function parameters.
          [\s]?                       # Allow one whitespace before (params).
          \(                          # Literal "(" params opening char.
          (?P<params>[^)]*)           # $6: == $params: function parameters.
          \)                          # Literal ")" params closing char.
        )?                            # End $5: Optional function parameters.
      )?                              # End $4: Optional BLOCK function.
      }                               # BLOCK tag closing literal "}"
    )                                 # End $1: == $opening: BLOCK start tag.
    (?P<contents>                     # $contents: BLOCK element contents.
      [^{]*                           # {normal) Zero or more non-"{"
      (?:                             # Begin {(special normal*)*} construct.
        \{                            # {special} Allow a "{" but only if it is
        (?!/?block:[a-z0-9\s_-]+\})   # not a BLOCK tag opening literal "{".
        [^{]*                         # More {normal}
      )*                              # Finish "Unrolling-the-Loop" (See: MRE3).
    )                                 # End $contents: BLOCK element contents.
    (?P<closing>                      # $closing: BLOCK element end tag.
      {                               # BLOCK tag opening literal "{"
      /block:                         # Closing BLOCK tag ident.
      (?P=name)                       # Close name must match open name.
      }                               # BLOCK tag closing literal "}"
    )                                 # End $closing: BLOCK element end tag.
    %six';

$text = file_get_contents('testdata.html');
if (preg_match($re, $text, $matches)) print_r($matches);
else echo("no match!");
?>

请注意,额外的缩进和注释可以让人真正了解正则表达式尝试执行的操作。我的测试显示正则表达式没有任何问题,它的工作方式与宣传的一样。它甚至实现了Jeffrey Friedl先进的“Unrolling-the-Loop”效率技术,所以无论谁写这个都有一些真正的正则表达式技能。

e.g。鉴于以下数据来自原始问题:

<ul>{block:menu}
    <li><a href="{var:link}">{var:title}</a>
{/block:menu}</ul>

以下是脚本的(正确)输出:

'''
Array
(
    [0] => {block:menu}
    <li><a href="{var:link}">{var:title}</a>
{/block:menu}
    [opening] => {block:menu}
    [1] => {block:menu}
    [inverse] =>
    [2] =>
    [name] => menu
    [3] => menu
    [4] =>
    [function] =>
    [5] =>
    [6] =>
    [params] =>
    [7] =>
    [contents] =>
    <li><a href="{var:link}">{var:title}</a>

    [8] =>
    <li><a href="{var:link}">{var:title}</a>

    [closing] => {/block:menu}
    [9] => {/block:menu}
)
'''

当测试数据中包含可选的functionparams时,它也可以使用。

那就是说,我对问题/正则表达式有一些问题:

  • 正则表达式混合了命名和编号的捕获组。
  • {}是元字符,应该进行转义(尽管PCRE能够正确地确定在这种情况下应该按字面解释它们)。
  • 目前还不清楚用户将如何使用可选的捕获组。
  • 目前还不清楚OP使用此正则表达式时遇到的问题。