preg匹配中间不包含相同标签的标签之间的文本

时间:2016-05-29 16:38:06

标签: php html regex tags pcre

我知道有几个类似的问题,但在这个具体案例中找不到任何问题。

我拿了一个代码并根据我的需要调整它,但现在我发现了一个我无法纠正的错误。

代码:

$tag = 'namespace';
$match = Tags::get($f, $tag);
var_dump($match); 

  static function get( $xml, $tag) { // http://stackoverflow.com/questions/3404433/get-content-within-a-html-tag-using-7-processing
// bug case      string(56) "<namespaces>
//      <namespace key="-2">Media</namespace>"
      $tag_ini = "<{$tag}[^\>]*?>"; $tag_end = "<\\/{$tag}>";
      $tag_regex = '/' . $tag_ini . '(.*?)' . $tag_end . '/si';

      preg_match_all($tag_regex,
      $xml,
      $matches,
      PREG_OFFSET_CAPTURE);
      return $matches;
  }

如您所见,如果标记是嵌套的,则存在错误:

  

<namespaces> <namespace key="-2">Media</namespace>

什么时候应该返回&#39; Media&#39;,甚至是外部的'<namespaces>',然后是内部的。{/ p>

我尝试添加&#34; <{$tag}[^\>|^\r\n ]*?>&#34;,^\s+,将*更改为*?,以及其他一些最好只能识别出错误案例的事情。

还尝试了"<{$tag}[^{$tag}]*?>",它给出了空白,我想它会使自己无效。

我是关于正则表达式的新手,我可以告诉我要解决此问题,只需要添加“不要打开相同类型的新标记”。 或者我甚至可以在我的用例中使用黑客答案,如果内部文本有新的线路运输则排除。

任何人都可以获得正确的语法吗?

您可以在此处查看文字摘录:http://pastebin.com/f2naN2S3

在提议的更改之后:$tag_ini = "<{$tag}\\b[^>]*>"; $tag_end = "<\\/{$tag}>";它适用于示例案例,但不适用于此案例:

<namespace key="0" />
      <namespace key="1">Talk</namespace>

导致:

<namespace key="1">Talk"

这是因为数字和&#34;和字母被认为是在单词边界内。我怎么能解决这个问题?

3 个答案:

答案 0 :(得分:1)

这可能不是想法的答案,但我正在搞乱一个正则表达式生成器:

<?php
# URL that generated this code:
# http://txt2re.com/index-php.php3?s=%3Cnamespace%3E%3Cnamespace%20key=%22-2%22%3EMedia%3C/namespace%3E&12&11

$txt='arstarstarstarstarstarst<namespace key="-2">Media</namespace>arstarstarstarstarst';

$re1='.*?'; # Non-greedy match on filler
$re2='(?:[a-z][a-z]+)'; # Uninteresting: word
$re3='.*?'; # Non-greedy match on filler
$re4='(?:[a-z][a-z]+)'; # Uninteresting: word
$re5='.*?'; # Non-greedy match on filler
$re6='(?:[a-z][a-z]+)'; # Uninteresting: word
$re7='.*?'; # Non-greedy match on filler
$re8='((?:[a-z][a-z]+))';   # Word 1

if ($c=preg_match_all ("/".$re1.$re2.$re3.$re4.$re5.$re6.$re7.$re8."/is", $txt, $matches))
{
    $word1=$matches[1][0];
    print "($word1) \n";
}

#-----
# Paste the code into a new php file. Then in Unix:
# $ php x.php
#-----
?>

答案 1 :(得分:1)

主要问题是您在开始标记后没有使用字边界,因此模式中的namespace也可能与namespaces标记以及许多其他标记匹配。

接下来的问题是,如果有一个自动关闭的<${tag}\b[^>]*>(.*?)<\/${tag}>标记后面跟着一个&#34;正常&#34;那么namespace模式就会过火。配对打开/关闭namespace标记。因此,您需要在(?<!\/)之前使用否定的lookbehind >(请参阅demo),或在(?![^>]*\/>)之后使用\b否定前瞻(请参阅{ {3}})。

所以,你可以使用

$tag_ini = "<{$tag}\\b[^>]*(?<!\\/)>"; $tag_end = "<\\/{$tag}>";

答案 2 :(得分:0)

这条线是我需要的

   $tag_ini = "<{$tag}\\b[^>|^\\/>]*>"; $tag_end = "<\\/{$tag}>";

非常感谢@Alison和@Wictor的帮助和指示