2010-08-01 49 views
5

我想通过一个标记分割一个HTML字符串,以创建一个博客预览而不显示完整的帖子。这比我第一次想到的更难。下面是问题:拼图:正确地分割一个HTML字符串

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

例子:

<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。正则表达式也许是另一种选择。你会用什么样的策略来解决这个问题,并尽可能地做到无懈可击?任何代码片段或提示也将不胜感激(我使用PHP)。

+7

正则表达式是**不是**选项。看到这个答案另一个SO问题:http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454 – You 2010-08-01 01:30:27

+0

为什么你不能只使用修剪()生成的字符串,找到缺少的开放或关闭元素并追加适当的,使其有效的HTML? – 2010-08-01 01:32:30

+0

@你如果正则表达式不是一个选项,请随意建议另一个可能无效(X)HTML的选项。据我所知,PHP没有XML解析器,它不会在无效XML上抛出错误,并且不是GPL许可的。 – VirtuosiMedia 2010-08-01 01:37:39

回答

2
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; 
} 

这条任何非HTML的READ_MORE()标记之后,并通过汽提相应标记,同时保持前开始并且标记之后结束任何标记它减少到最小:

<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> 
+0

我现在正在测试这个,mvds。 – VirtuosiMedia 2010-08-01 01:56:40

+0

谢谢,mvds,这个效果很好。如果我使用你的函数是否可以,如果是这样,你希望如何在代码中记入? – VirtuosiMedia 2010-08-01 02:08:15

+0

按照您认为合适的方式使用它,而对于学分,优先不使用它。顺便说一句,你还需要去掉'〜[^ <>] + $〜'(最后一个标签后面的所有内容),也许还有类似'〜] *>〜'的标签。 – mvds 2010-08-01 02:12:01

0

为什么不使用两个textareas?削减的上方和下方?应该让用户明白发生了什么事情,并消除您的头痛。

如果你想要使用一个令牌,你应该选择一些更有特色的东西。也许:<!--full body cut-->你可以更确定一点,实际上并不是内容被误认为是令牌。

总之,如果你想拆就令牌的字符串,你只需要揣摩出你的令牌使用strpos()然后用substr()砍掉的第一部分。喜欢的东西:

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

在此之后,运行$intro通过tidy(PHP扩展)clean up the syntax,然后去掉多余的废话它在那里补充道。 (我想你可以使用空字符串str_replace()临时演员。)

+0

不幸的是,Tidy似乎并不是一个有效的选项,因为它可能不会在所有PHP主机上安装或启用。(该项目将分发)。但是,我不确定Tidy的可用性程度,所以如果我错了,请随时纠正我。两个textareas肯定能够解决这个问题,但我试图保持用户界面的亮度,如果可能的话,所以我想先探索其他选项。 – VirtuosiMedia 2010-08-01 01:53:18

1

我目前看到的唯一正确选项是在PHP中编写自己的上下文无关语法HTML解析器,它将允许您适当地关闭标记(只需通过弹出当达到阅读更多()和每个弹出添加一个结束标签堆栈)。

这一点,但是,大量的工作,这可能会为你工作得很好:

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

你失去的HTML标记,但它的死很容易实现。并且没有可能的XSS在您的头版:)

+0

丢失HTML标记是不可选的,但谢谢您的建议。 – VirtuosiMedia 2010-08-01 01:45:56

+0

+1关于编写解析器的第一段 - 这就是我为自己的博客所做的。它基本上从头开始经过文本,并保留当前打开的HTML标签的堆栈,然后一旦确定了要破解文本的位置,它会追加需要的任何结束标签。我的情况稍微复杂一些,因为我没有一个明确的标记来标记拆分 - 它是用Python编写的 - 但如果你愿意,我愿意分享代码。 – 2010-08-01 02:14:48

+0

啊,没关系,我看你有更好的东西 – 2010-08-01 02:17:43

1

而不是使用完整的HTML,为什么不使用可以生成HTML的许多标记语言之一,但不需要关闭标签等。更容易训练用户,并且会避免接受原始HTML允许的XSS攻击的所有可能性。

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

+0

这是CMS的管理部分,所以我宁愿尽可能少地学习曲线。我选择了CKEditor,因为它比Markdown编辑器功能更丰富一些,它使非技术用户可以更接近Word。我正在过滤输入。还是)感谢你的建议。 – VirtuosiMedia 2010-08-01 01:59:53

+0

因此......考虑到WordPress,Drupal,Joomla和其他一些开源CMS系统的可用性,为什么你要写另一个?只是好奇。 – 2010-08-01 10:57:44

1

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

为什么不能只在结果字符串中使用trim(),找到缺少的open或close元素并进行相应的追加,使其成为有效的HTML?

只是向前和向后查找下一个打开/关闭元素,并修复您的HTML。

所以,你可以在字符串中前后移动以获得下一个<>,如果这是一个HTML元素,那么在那里停下来,否则继续前进。

理想情况下,您应该每次提交需要处理一次,所以您不断付出代价来执行此操作。

UPDATE:

我忘了包括一个链接,以帮助strpos

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

1

PHP整洁是一种重量很轻,高效的工具来修复无效的标记。 看看,我已经使用它并在我的应用程序中进行了基准测试,并且效果很好。 Moreoever它有很多的配置选项,以满足您的需要选择最好的,也照顾得象编码等可能出现的问题,嵌套无效标签等

请参阅参考: 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> 

    ?>