2007年06月29日

HTML文章中截取摘要的问题

作者 非鱼

博客系统通常的做法是,在博客的首页只显示文章的摘要,点击标题进入以后查看全文。显示哪一部分作为摘要是个比较特殊的问题,不同的系统都有自己不同的处理方式,有的是将摘要和扩展部分作为两个输入框,由用户自己决定哪些部分作为摘要,而且上下两部分都是完整的HTML,不存在截取的问题,就像本站使用的Serendipity系统。还有一种像WordPress,你可以自己在正文区中插入一个<!–More–>的标记,显示博客列表页的时候,会对文章以这个标记进行截取。不过我没有试过在一个Table的中间插入这个标记会怎么样,不过这种做法也是完全让用户自主决定如何截取文章,即使截错了,用户重新设置一下就OK了。

而我们公司的博客系统在设计之初,被很多人的意见所左右,(主要是许多没有真正写过博客的人的意见),意见是使用两个框的方式太麻烦,插入标记大部分恐怕也搞不懂,我们要给用户提供最简单的操作,用户只要写他的文章,如何截取交给系统就可以了。由此,让我陷入了一个疯狂改Bug的循环。

从整段的HTML中截取400个字作为摘要,如何处理?也许删除所有<>标签是最简单的办法,但是,博客的文字大家还是想要保留个性,而且有人想使用大字体,或者想对某篇文章使用大字体,以便更加显著,等等诸如此类的需求。总之,用户只想让自己的博客更漂亮,才不会管你怎么截取。

如果只是简单的按照400个字来截取,那么很可能会截到一个标签的中间,这半个标签可能就会导致后面的大段文本被作为标签内文字而不显示,直到遇到下一个结束符为止。于是首先要根据<>标记符,确保要截取的位置不在这两个标记中间。具体的做法是,找到一个<符号,看看里面的标记,去找它所对应的末尾标记,比如找到<p,就去找</p>,找到<div,就去找</div>。随后又发现,如果截取了半个Table,整个页面的布局就会错位,博客的侧边栏都跑到正文的下面去了,因为被作为Table里面的单元格来处理了。想来想去,决定如果出现Table的话,就在Table之前截断,结果导致许多文章的摘要里一个字都没有。然后又发现,不止Table会破坏布局,如果div的前后不对应,布局也会错位,因为我们的博客模板使用的是Div布局。用前面的截取方法存在一个问题,因为Div是可以嵌套的,连续的两个<div><div>标记,就会出现遇到第一个div标记的时候,直接跳到了第一个结尾的</div>标记上去,结果第二个<div>就没有结束符了。除此之外,font, span等等都是可以嵌套的。

后来又想了个办法,干脆把所有的div, font, span全部过滤掉,所<p></p>全部换成br,终于,基本上不会出现截错的问题了,但是,许多文章的分段和缩进是在div里面使用style来定义的(不知道这些用户的文章是从哪里拷过来的),结果许多人的博客首页文字都变成了一样的12px文字,而且密密麻麻挤在一起,极其不美观。再后来,还是有个用户报怨自己博客页面出错,无法打开。进去一看,他的博客文章里居然有N多<!–[IF[之类的标记,出现这种符号以后,用来截取的正则表达式就会报错。于是又在截取之前将注释类符号全部删掉。过了两天,他又报错,原来文章里还有<![IF这样的标签。God,你从哪里复制来的文章啊?

没办法了,即要保留格式,又要兼容各种不可预期的标签结构,这个问题一定要解决。

思路:想在不破坏格式的情况下截取文字,最保险的方法是,保留所有的格式。于是想到一个办法,只对文本内容使用字数限制,而留下所有的HTML标记。比如要取400个字,那么,从正文的第一个字符开始算,如果是普通的文本,放入结果变量,并将记数器加1。如果记数器已经到了400,就忽略这个字。如果字符是<,那么将其后所有的文本放入结果变量,直到>为止。这样,最后截取出来的摘要包括了400个文字,和一堆格式完整的HTML标签。(前提是他贴进来的时候HTML本来就是完整的)。

但是,如果在达到400字以后后面还有一些br, p, li的话,摘要后面就会出现很大的一段空白,那当然是不行的。因此还要把这些东西处理掉,数p的个数太麻烦,把p变成br,如果记数器已经到了400,忽略掉所有的br标签。对li也是,但是处理要特殊一点,因为不能在两个li中间截断。同理,可以对<tr><td>做同样的处理,这样就不会出现半个空白的表格了。

总体使用下来,这个效果还是可以接受的。虽然源代码里多了很多无用的代码(特别是那些从Word里面粘贴文章过来的人),但是从表面上看,截取是比较完美的。

附完整的C#代码:点击浏览或右键下载 (07年6月28号版本)