【问题标题】:Select the first paragraph tag not contained in within another tag using RegEx (Perl-style)使用 RegEx(Perl 样式)选择不包含在另一个标签中的第一个段落标签
【发布时间】:2012-01-19 19:04:12
【问题描述】:

我有这个 html 块:

<div>
  <p>First, nested paragraph</p>
</div>
<p>First, non-nested paragraph.</p>
<p>Second paragraph.</p>
<p>Last paragraph.</p>

我正在尝试选择该块中的第一个非嵌套段落。我正在使用 PHP(perl 风格)的 preg_match 来查找它,但似乎无法弄清楚如何忽略 div 中包含的 p 标签。

这是我目前所拥有的,但它选择了上面包含的第一段的内容。

/<p>(.+?)<\/p>/is

谢谢!

编辑

不幸的是,我没有 DOM Parser 的奢侈。

我非常感谢不使用 RegEx 解析 HTML 的建议,但这并没有真正帮助我的特定用例。我有一个非常受控的情况,内部应用程序生成结构化文本。如果某些文本匹配某个模式,我正在尝试替换它。这是一个简化的案例,我试图忽略嵌套在其他文本中的文本,而 HTML 是我能想到解释的最简单的案例。我的实际案例看起来有点像这样(但更多的数据和缩小):

#[BILLINGCODE|12345|11|15|2001|15|26|50]#
[ITEM1|{{Escaped Description}}|1|1|4031|NONE|15]
#[{{Additional Details }}]#
[ITEM2|{{Escaped Description}}|3|1|7331|NONE|15]
[ITEM3|{{Escaped Description}}|1|1|9431|NONE|15]
[ITEM4|{{Escaped Description}}|1|1|5131|NONE|15]

我必须将某些行的某一列重新格式化为大量类似的行。帮助我的第一个问题将有助于实际项目。

【问题讨论】:

  • 笑话链接一次相关! (请注意,不要重复)
  • 我假设您发布的块包含在其他元素中?
  • 询问如何使用正则表达式解析 HTML?你会因此被活活吃掉……不过说真的,DOMDocument 会在这里做得更好。
  • 嵌套段落总是缩进吗?段落只会跨越一行吗?在那一行上只会有一个段落吗?如果是这样,只需在行首查找开始标签并匹配整行。 /^&lt;p&gt;.+/m 如果这还不够,请详细说明您的要求
  • 添加了说明。我有一些非常混乱的数据要挖掘,因此非常感谢非开玩笑的答案。

标签: php regex perl


【解决方案1】:

您的正则表达式不起作用。即使您只有非嵌套段落,您的捕获括号也会匹配 First, non-nested ... Last paragraph.

试试:

&lt;([^&gt;]+)&gt;([^&lt;]*&lt;(?!/?\1)[^&lt;]*)*&lt;\1&gt;

如果\1p,则获取\2

但是,恕我直言,HTML 解析器会做得更好。

【讨论】:

  • 试运行,肯定会选择“第一,嵌套段落”。 ——“第一,非嵌套……最后一段。”如果您选择“?”,将被选中我所拥有的
  • 糟糕,是的,我没有看到?。但是我讨厌懒惰的量词,也许我只是没有在脑海中看到它;)
  • 感谢您的相关回答,我添加了一些说明,以便更清楚地了解为什么我使用 HTML 示例来解决这个问题。我正在使用您的示例并取得进展。谢谢。
  • 虽然我看到了您的数据,但我想知道您为什么首先尝试匹配 您不感兴趣的行?分治...
  • 如果它们实际上在单独的行上,他们会这样做:/ 很多这种混乱被缩小了,其中一些文件是单行的。一团糟。尽管我可能必须执行几个步骤(如您所说,分而治之)才能充分理解其中一些文件。我没有将我的整个解析任务介绍给 SO,而是介绍了示例。 ]s 和结束 #s 基本上可以被视为换行符。
【解决方案2】:

这样的事情怎么样?

<p>([^<>]+)<\/p>(?=(<[^\/]|$))

进行前瞻以确保它不在结束标记内;但可以在字符串的末尾。可能有更好的方法来查找段落标签中的内容,但您需要避免过于贪婪(.+? 不够)。

【讨论】:

    【解决方案3】:

    使用三步流程。首先,祈祷一切都很好。其次,首先,删除所有嵌套的内容。

    s{<div>.*?</div>}{}g;         # HTML example
    s/#.*?#//g;                   # 2nd example
    

    然后得到你的结果。剩下的所有内容现在都没有嵌套。

    $result = m{<p>(.*?)</p>};    # HTML example
    $result = m{\[(.*?)\]};       # 2nd example
    

    (这是 Perl。不知道在 PHP 中会有什么不同)。

    【讨论】:

      【解决方案4】:

      “你不应该使用正则表达式来解析 HTML。”

      这是每个人都说的,但没有人真正提供如何实际去做的例子,他们只是宣扬它。好吧,感谢Levi Morrison 的一些激励,我决定阅读DomDocument 并弄清楚如何去做。

      对于所有说“哦,学习解析器太难了,我只会使用正则表达式”的人。好吧,我以前从来没有用过 DomDocument 或 XPath,这花了我 10 分钟。去阅读 DomDocument 上的文档并按照你应该的方式解析 HTML。 p>

      $myHtml = <<<MARKUP
         <html>
             <head>
                  <title>something</title></head>
             <body>
                  <div>
                      <p>not valid</p>
                  </div>
                  <p>is valid</p>
                  <p>is not valid</p>
                  <p>is not valid either</p>
                  <div>
                      <p>definitely not valid</p>
                  </div>
             </body>
         </html>
      MARKUP;
      
      $DomDocument = new DOMDocument();
      $DomDocument->loadHTML($myHtml);
      $DomXPath = new DOMXPath($DomDocument);
      $nodeList = $DomXPath->query('body/p');
      $yourNode = $DomDocument->saveHtml($nodeList->item(0));
      
      var_dump($yourNode)
      
      // output '<p>is valid</p>'
      

      【讨论】:

        【解决方案5】:

        您可能想看看 this post 关于使用 Regex 解析 HTML。

        因为 HTML 不是一种正则语言(正则表达式也是),所以您不能使用 Regex 删除任意 HTML 块。使用 HTML 解析器,它会比尝试拼凑一些正则表达式更顺利地完成工作。

        【讨论】:

        • (不否认任何人的娱乐。但即使是非开玩笑的答案也大多是错误的。所以请在标记主题时作为评论或社区维基发表)。正则表达式,尽管有名称,但不是正则表达式。它们是上下文无关的。只是努力是 prohibitive 使其对任意 html 都可靠。
        猜你喜欢
        • 2010-11-11
        • 1970-01-01
        • 2011-03-31
        • 1970-01-01
        • 2015-08-14
        • 2020-02-19
        • 1970-01-01
        • 2017-02-21
        • 2017-05-04
        相关资源
        最近更新 更多