代码之家  ›  专栏  ›  技术社区  ›  VirtuosiMedia

拼图:正确拆分HTML字符串

  •  5
  • VirtuosiMedia  · 技术社区  · 14 年前

    我试图用一个标记拆分一个HTML字符串,以便在不显示完整日志的情况下创建博客预览。有点 更努力 比我最初想的要多。问题如下:

    • 用户将创建HTML 通过Wysiwyg编辑器(Keckeditor)。 加价不能保证 相当或一致。
    • 令牌, 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。Regex可能是另一种选择。你会用什么策略来解决这个问题并使其尽可能防弹?任何代码片段或提示也会受到赞赏(我使用的是PHP)。

    6 回复  |  直到 13 年前
        1
  •  2
  •   mvds    14 年前
    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>
    
        2
  •  1
  •   Karel Petranek    14 年前

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

    然而,这是一个很大的工作,这可能对您很好:

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

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

        3
  •  1
  •   Craig Trader    14 年前

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

    PHP Markdown 似乎是一个明显的适合,特别是鉴于您的愿望,避免GNU的GPL。

        4
  •  1
  •   James Black    14 年前

    为了回答对我的评论的评论,我决定让它成为一个答案,这样我就可以利用标记选项。

    为什么不能只在结果字符串上使用trim(),找到缺少的open或close元素并适当地附加它,使其成为有效的HTML?

    只需前后移动即可找到下一个打开/关闭元素,并修复HTML。

    所以,你可以在绳子上前后移动,以获得下一个 < > ,如果这是一个HTML元素,那么就停在那里,否则继续。

    理想情况下,每次提交时都需要处理一次,因此您要一直为执行此操作支付代价。

    更新:

    我忘了提供一个帮助的链接 strpos :

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

        5
  •  1
  •   DhruvPathak    13 年前

    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>
    
        ?>
    
        6
  •  0
  •   cbednarski    14 年前

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

    如果你 想要使用一个令牌,你应该选择一些更独特的东西。也许吧: <!--full body cut--> 你可以更确定的是,它并没有被误认为是一种象征。

    无论如何,如果您想拆分令牌上的字符串,您只需确定令牌在哪里 使用 strpos() 然后使用 substr() 把第一部分切掉。类似:

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

    接下来,运行 $intro 通过 tidy (PHP扩展)到 clean up the syntax 然后去掉它在里面增加的垃圾。(我想你可以 str_replace() 带有空字符串的附加项。)