查看正则表达式断言以匹配嵌套HTML表

时间:2013-12-22 20:48:24

标签: regex regex-lookarounds

我希望匹配表格中的特定表格。这是示例html和迄今为止失败尝试的摘要:

<table id="parent">
    <table class="possible_target">
            <tr><td>We're tageting this table</td></tr>
    </table>
</table>
<table class="possible_target">
    <tr><td>We're not targeteing this table</td></tr>
</table>

这是我最初的尝试。但即使它奏效了,它也可能与第二张未通过的表格相匹配:

~(?=<table.*?)<table class="possible_target".*?</table>~si

这是我对我想要完成的事情的sudo表达。在进行匹配之前,它会声明是否存在开始表标签以及没有关闭表标签:

~(?=<table.*?)(?!</table>)<table class="possible_target".*?</table>~si

2 个答案:

答案 0 :(得分:2)

我发现这很有趣,因为使用正则表达式和嵌套的html标签很有挑战性。

我的尝试确实(应该)以下内容:

1。)使用回调函数按深度枚举表。最低深度= 1

// html stuff to process
$source = "your input";

// specify tag to match
$rx_tag = "table";

// match all $rx_tag in depth (lowest = 1)
$rx_depth = 2;

// ----------------------------

// set markers using callback function
$source = preg_replace_callback('~<(/)?'.$rx_tag.'~i','set_tag_depth',$source);

function set_tag_depth($out)
{
  global $tag_depth;

  if($out[1]=="/") {
    $tag_depth--; return $out[0].($tag_depth+1);
  }

  $tag_depth++; return $out[0].$tag_depth;
}

#echo nl2br(htmlspecialchars($source));

2。)现在将表重命名为深度,例如对于<table2 ... </table2>内的表格,<table1, <table3 ... </table3>内的所有表格均为<table2,依此类推。现在很容易匹配所需深度的表格。然后剥离枚举,以防再次需要原始源。

// get specified tags in desired depth
$pattern = '~<'.$rx_tag.(string)$rx_depth.'.*</'.$rx_tag.(string)$rx_depth.'>~Uis';
preg_match_all($pattern,$source,$out);

// strip markers
if(!empty($out[0]))
{
  foreach($out[0] AS $v)
  {
    $v = preg_replace('~(</?'.$rx_tag.')\d+~i','\1',$v);

    // test output
    echo nl2br(htmlspecialchars($v))."<br>------------------------------<br>";
  }
}

$source = preg_replace('~(</?'.$rx_tag.')\d+~i','\1',$source);

如果标签包含相同类型的标签,那么这些标签不会从父母标签中删除,例如<table2>...</table2&GT;可能包含<table3>...</table3> ... <table3>...</table3>。设置$rx_depth = 3;以获取这些内容。

我希望它能够正常工作,已经非常疲惫了:-)它的设计适用于任何类型的标签,但没有进行太多测试。至少是一个想法。

答案 1 :(得分:1)

使用同名的父/兄弟标签搜索平衡文本的问题 仅在属性上有所不同。

也就是说,你可以一次性收集所有潜在的父母候选人平衡标签 然后在另一个通道中搜索“可能目标”的候选人。

需要2个几乎相同的正则表达式(仅按属性不同) 有关详细信息,请参阅扩展的正则表达式(底部)。

PHP示例代码

// PHP sample code

 $html = 
 '
 <table id="parent">
    <table class="possible_target">
        C table data
    </table>
    <table class="possible_target">
        D table data
    </table>
 </table>

 <table id="parent">
   <table>
     <table class="possible_target">
              <tr><td>We\'re targeting this table</td></tr>
     </table>
   </table>
 </table>

 <table class="possible_target">
     <tr><td>We\'re not targeteing this table</td></tr>
 </table>
 '
 ;

 // Regexes -
 $rx_Parent = '~(?s)<table\s+id="parent">((?<Table_Core>(?:(?>(?:(?!</table\s*>|<table[\s>][^>]*(?<!/)>).)*)|(?<New_Table><table(?!\s+id="parent">)[\s>][^>]*(?<!/)>(?&Table_Core)</table\s*>))*))</table\s*>~';
 $rx_Target = '~(?s)<table\s+class="possible_target">((?<Table_Core>(?:(?>(?:(?!</table\s*>|<table[\s>][^>]*(?<!/)>).)*)|(?<New_Table><table(?!\s+class="possible_target">)[\s>][^>]*(?<!/)>(?&Table_Core)</table\s*>))*))</table\s*>~';

 // Match all possible parent candidates -
 if ( preg_match_all ( $rx_Parent, $html, $ParentMatches, PREG_PATTERN_ORDER ) )
 {
    print "\n============================\n";
    print_r( $ParentMatches[0] );
    print "\n\n";

    foreach( $ParentMatches[0] as $parent )
    {
        // Match each individual parent candidate possible targets -
        if ( preg_match_all ( $rx_Target, $parent, $TargetMatches, PREG_SET_ORDER ) )
        {
            print "\n-----------------\n>Found Valid Parent\n";
            foreach( $TargetMatches as $target )
            {
                print "Target:\n'" . $target[0] . "'\n";   // group 0
                print "Core = \n'" . $target[1] . "'\n";   // group 1
            }
        }
    }
 }
 else
 {
    print "No parents\n";
 }

输出&gt;&gt;

 ============================
 Array
 (
     [0] => <table id="parent">
    <table class="possible_target">
        C table data
    </table>
    <table class="possible_target">
        D table data
    </table>
 </table>
     [1] => <table id="parent">
   <table>
     <table class="possible_target">
              <tr><td>We're targeting this table</td></tr>
     </table>
   </table>
 )



 -----------------
 >Found Valid Parent
 Target:
 '<table class="possible_target">
        C table data
    </table>'
 Core = 
 '
        C table data
    '
 Target:
 '<table class="possible_target">
        D table data
    </table>'
 Core = 
 '
        D table data
    '

 -----------------
 >Found Valid Parent
 Target:
 '<table class="possible_target">
              <tr><td>We're targeting this table</td></tr>
     </table>'
 Core = 
 '
              <tr><td>We're targeting this table</td></tr>
     '

扩展正则表达式

 # BalancedText_PHP_Html.rxf
 # Processed by  RegexFormat4 (http://www.regexformat.com)

 # '~(?s)<table\s+id="parent">((?<Table_Core>(?:(?>(?:(?!</table\s*>|<table[\s>][^>]*(?<!/)>).)*)|(?<New_Table><table(?!\s+id="parent">)[\s>][^>]*(?<!/)>(?&Table_Core)</table\s*>))*))</table\s*>~'

 (?s)                          # Dot-All

 # Parent Table                                   
 # ==================

 <table \s+ id="parent">       # Parent table start
 (                             # (1 start), Core Start

      (?<Table_Core>                # (2 start), Table Core
           (?:
                (?>
                     (?:
                          (?!                           # Not start/end of another table
                               </table \s* >
                            |  
                               <table [\s>] [^>]* 
                               (?<! / )
                               >
                          )
                          . 
                     )*
                )
             |  
                (?<New_Table>                 # (3 start), New Table                    
                     <table                        # Table start
                     (?! \s+ id="parent"> )        # but, not a parent table type
                     [\s>] [^>]* 
                     (?<! / )
                     >
                     (?&Table_Core)                # Recurse Table Core
                     </table \s* >                 # Table end
                )                             # (3 end)
           )*
      )                             # (2 end)


 )                             # (1 end), Core End
 </table \s* >                 # Parent table end


 # ==========================================================================

 (?s)                                   # Dot-All

 # Target Table                                   
 # ==================

 <table \s+ class="possible_target">    # Target table start
 (                                      # (1 start), Core Start
      (?<Table_Core>                         # (2 start), Table Core
           (?:
                (?>
                     (?:
                          (?!                                    # Not start/end of another table
                               </table \s* >
                            |  
                               <table [\s>] [^>]* 
                               (?<! / )
                               >
                          )
                          . 
                     )*
                )
             |  
                (?<New_Table>                          # (3 start), New Table                    
                     <table                                 # Table start
                     (?! \s+ class="possible_target"> )     # but, not a target table type
                     [\s>] [^>]* 
                     (?<! / )
                     >
                     (?&Table_Core)                         # Recurse Table Core
                     </table \s* >                          # Table end
                )                                      # (3 end)
           )*
      )                                      # (2 end)
 )                                      # (1 end), Core End
 </table \s* >                          # Target table end