【问题标题】:Puzzle: Splitting An HTML String Correctly谜题:正确拆分 HTML 字符串
【发布时间】:2011-03-23 18:21:24
【问题描述】:

我正在尝试通过标记拆分 HTML 字符串,以便在不显示完整帖子的情况下创建博客预览。这比我最初想象的要困难。以下是问题:

  • 用户将创建 HTML 通过所见即所得的编辑器(CKEditor)。 不保证标记是 漂亮或一致。
  • 令牌read_more()可以 放置在字符串的任何位置, 包括嵌套在一个 段落标签。
  • 生成的第一个拆分字符串 需要对所有人都是有效的 HTML 令牌的合理使用。

可能的用途示例:

<p>Some text here. read_more()</p>

<p>Some text read more() here.</p>

<p>read_more()</p>

<p>  read_more()</p>

read_more()

到目前为止,我已经尝试在令牌上拆分字符串,但它留下了无效的 HTML。正则表达式也许是另一种选择。您将使用什么策略来解决这个问题并使其尽可能防弹?任何代码 sn-ps 或提示也将不胜感激(我正在使用 PHP)。

【问题讨论】:

  • 正则表达式不是选项。请参阅另一个 SO 问题的答案:stackoverflow.com/questions/1732348/…
  • 为什么你不能只在结果字符串上使用 trim(),找到丢失的打开或关闭元素并适当地附加,使其成为有效的 HTML?
  • @You 如果 regex 不是一个选项,请随意提出另一个适用于潜在无效 (X)HTML 的选项。据我所知,PHP 没有一个不会在无效 XML 上引发错误的 XML 解析器,也没有 GPL 许可。
  • @James Black - 这可能是一种选择,但找到丢失的结束标签的最佳方法是什么?
  • DOMDocument::loadHTML() 呢?

标签: php regex string html-parsing


【解决方案1】:

PHP tidy 是一个非常轻量且高效的修复无效标签的实用程序。 看看,我已经在我的应用程序中使用过它并对其进行了基准测试,效果很好。 此外,它有许多配置选项可以最好地满足您的需求,并处理其他可能的问题,如编码、嵌套无效标签等。

查看参考: http://www.php.net/manual/en/tidy.cleanrepair.php

示例用法:

<?php

    function tidyString($str)
    {
      $config = array('show-body-only' => true); /* else it adds HTML tags too */
      tidy_set_encoding('utf8');
      $outStr = tidy_repair_string($str,$config);
      return $outStr;
    }


    $inStr = "<span> this is my incorrect html</spa";
    echo tidyString($inStr);  // Output : <span>this is my incorrect html</span>

    ?>

【讨论】:

    【解决方案2】:

    为了回答对我的评论的评论,我决定将其作为答案,这样我就可以利用标记选项。

    为什么你不能只对结果字符串使用 trim(),找到丢失的打开或关闭元素并适当地附加它,使其成为有效的 HTML?

    只需前后遍历即可找到下一个打开/关闭元素,然后修复您的 HTML。

    因此,您可以在字符串中前后移动以获取下一个 &lt;&gt;,如果这是一个 HTML 元素,则停在那里,否则继续。

    理想情况下,您应该每次提交都需要处理一次,因此您需要继续付出代价来执行此操作。

    更新:

    我忘了包含一个链接来帮助strpos

    http://tuxradar.com/practicalphp/4/7/5

    【讨论】:

      【解决方案3】:

      与其使用完整的 HTML,不如使用可以生成 HTML 但不需要关闭标签等的众多标记语言中的一种。这样会更容易培训您的用户,并且会避免所有接受原始 HTML 允许 XSS 攻击的可能性。

      PHP Markdown 似乎很合适,尤其是考虑到您希望避免使用 GNU GPL。

      【讨论】:

      • 它用于 CMS 的管理部分,所以我希望学习曲线尽可能少。我选择 CKEditor 是因为它比 Markdown 编辑器功能更丰富,并且它允许非技术用户更接近 Word。我正在过滤输入。不过,感谢您的建议。
      • 所以...鉴于 WordPress、Drupal、Joomla 和许多其他开源 CMSystems 的可用性,您为什么还要编写另一个?只是好奇。
      【解决方案4】:

      我目前看到的唯一正确的选择是在 PHP 中编写自己的上下文无关语法 HTML 解析器,这将允许您适当地关闭标签(只需在到达 read more() 时弹出堆栈并为每个弹出添加关闭标签)。

      然而,这需要做很多工作,这可能对您很有效:

      $stripped = strip_tags($input);
      list($preview) = explode("read more()", $stripped);
      

      您丢失了 HTML 标记,但它很容易实现。而且您的首页上没有可能的 XSS :)

      【讨论】:

      • 丢失 HTML 标记是一个不可选项,但感谢您的建议。
      • 关于编写解析器的第一段 +1 - 这就是我为自己的博客所做的。它基本上从头开始遍历文本并保留一堆当前打开的 HTML 标记,然后一旦确定在哪里中断文本,它就会附加任何必要的结束标记。我的有点复杂,因为我没有明确的标记来标记拆分 - 它是在 Python 中 - 但如果你愿意,我愿意分享代码。
      【解决方案5】:

      为什么不使用两个文本区域?切口上方和下方一个?应该让用户清楚地知道发生了什么,并为您消除头痛。

      如果您确实想使用令牌,您应该选择一些更有特色的东西。也许:&lt;!--full body cut--&gt;,您可以更确定的是,实际上内容并没有被误认为是令牌。

      无论如何,如果你想拆分令牌上的字符串,你只需要使用strpos() 找出你的令牌 的位置,然后使用substr() 将第一部分切掉。比如:

      $intro = substr($text, 0, strpos($string, $token));
      

      然后,将您的$intro 运行到tidy(PHP 扩展)到clean up the syntax,然后去掉它在那里添加的额外废话。 (我认为您可以str_replace() 使用空字符串的附加功能。)

      【讨论】:

      • Tidy,不幸的是,它似乎不是一个有效的选项,因为它可能没有在所有 PHP 主机上安装或启用。 (此项目将分发)。但是,我不确定 Tidy 的可用性范围,所以如果我错了,请随时纠正我。两个文本区域肯定可以解决问题,但如果可能的话,我会尽量保持用户界面简洁,所以我想先探索其他选项。
      【解决方案6】:
      function stripmore($in)
      {
          list($p1,$p2) = explode("read_more()",$in,2);
      
          $pass1 = preg_replace("~>[^<>]+<~","><",$p2);
          $pass2 = preg_replace("~^[^<>]+~","",$pass1);
      
          $pass3 = null;
          while ( $pass3 != $pass2 )
          {
              if ( $pass3 !== null ) $pass2 = $pass3;
              $pass3 = preg_replace("~<([^<>]+)></\\1>~","",$pass2);
          }
      
          return $p1."read_more()".$pass3;
      }
      

      这会去除 read_more() 标记之后的所有非 html,并通过去除相应的标记将其减少到最小,同时保留标记之前和之后的任何标记:

      <p>Some text here. read_more()</p>
            ==> <p>Some text here. read_more()</p>
      
      <p>Some <b>text</b> read_more() <b>here</b>.</p>
            ==> <p>Some <b>text</b> read_more()</p>
      
      <p>Some <b>text read_more() here</b>.</p>
            ==> <p>Some <b>text read_more()</b></p>
      

      【讨论】:

      • 谢谢,mvds,这很好用。如果我使用你的函数可以吗?如果可以,你希望如何在代码中获得信用?
      • 在你认为合适的时候使用它,至于学分,最好不要。顺便说一句,您还需要删除 ~[^&lt;&gt;]+$~ (最后一个标签之后的所有内容),也许还需要像 ~&lt;img[^&lt;&gt;]*&gt;~ 这样的标签。
      • 谢谢,非常感谢您的帮助。
      猜你喜欢
      • 1970-01-01
      • 2012-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多