最近想到该更新下自己网站了,好吧,就从那个我已经看不惯很久的msn space同步转发器开刀...
如果你是先通过space看到这篇文章的话,应该就能看到效果了。撇去显示效果不说,这里提一个有趣的问题:
换句话说,假设有下面这段html代码:
<p>this is a truncated html source<a href=\"some _fcksavedurl="\"some" URL\">some hyperlink</a>.<BR>
more text</p><p>one more</p>
出于某种需要,希望只显示(保留)这段代码的前n个字节。这样的需求其实不少见,最明显的就是在msn space文章同步的时候,我只希望对文章的大致内容转发过去,目前的做法就是截取文章的前1000字节。
如果仅仅是对一段普通文本进行这样的处理,那实在简单不过。直接截取即可。但是html却不同。
例如,对于上面的文字,如果截取的部分是:
今后将这段代码用于其它页面将出现不可能预见的问题。
考虑下面的代码:
<p>Some html code</p>
The following is a truncated article:<div><%=truncated_text%></div>
将上面被强制截断的代码片断填充到这段代码可能会让好好的html页面面目全非。
因此,我们需要"优雅"的去截断这段html。
何谓"优雅"?
造成上述问题的根本原因在于对html的强制截断忽略了html的结构信息,使得后面浏览器的分析操作出现紊乱。
因此,优雅的去进行html截断必须在截断后仍旧维持当前的html结构信息。
可能你会说,这还不简单,直接实现一个html parser或者用xml dom分析器来完成此事。的确可以,不过这样的效率和代价实在有点可怕了。因此,要完成这个优雅的截断操作,同时需要在执行效率以及资源消耗间做取舍。同时,尽量避免动辄用dom parser的恶俗。
寻找一种刚好能解决此问题,且保证效率较高并尽量避免滥用资源的途径是我们需要的。
我的方法
相信在这个所谓的什么"web2.0" "web3.0?"的扯淡名词时代恐怕已经有人实现过类似的东西,所以这里仅说说我目前的实现方案。如果你知道有更好的办法,希望能和大家分享。
撇开具体实现,这个问题其实很简单,就是一个配对问题。用一个stack来跟踪html标签的使用情况,每个<tag>应该有一个对应的</tag>配对。因此我们只要扫描一遍html,堆栈上残留的元素就是没有配对的tag。这些tag就是因为我们的暴力截取而成为孤对的。因此我们只要在html代码末尾FILO的补上这些这些tag的对应/tag即可。
但是还有些细节问题。
- 因为html没有xml规范,所以一些情况下未配对的tag也是允许的,例如<BR>。
- 存在单一自配对的<tag/>例如<img xxx />。
同时,对于暴力截断的html代码,我们不能奢望它们正好截断在一对tag之间,就像上面给出的例子那样,对于末尾是
的情况,我们还首先需要将这些“残缺”的tag给删去,否则即使配对仍旧存在问题。
大致的思路就是这样,关键就是如何实现的问题了。这里我仅采用javascript的String.replace方法进行上述的html扫描。
具体实现
实际上实现的困难在于如何去识别html的tag。这里的底线是不能用现有的dom parser。Javascript的正则式功能在这里就能派上大用处了。根据上面的分析,为了进行tag匹配的分析,我们需要识别出html中的所有tag。
他们存在下面几种形式
- < tagname some_attr>
- < / tagname>
- < tagname />
我们的任务是编写正则式去查找这些tag。并且捕获到tagname。这里用到了一个偏方,利用String.replace方法。
一般,很多人都这样用它some_str.replace(/some regexp/, "new text");
实际上,他有另一个形式:
some.replace(/some regexp/, function( match_text)
{
return "new text";
});
利用一个匿名函数进行替换...
实际上,这个replace是一个再好不过的基于正则式的查找操作手段。因为相比手写一个查找循环,他的迭代操作是在解析器内部实现的,效率肯定要高很多。同时据我所知,要写个循环依次查找每个匹配正则式的循环很不容易,貌似没法指定搜索的开始字符。
好了,到此关键问题解决了。试验证明用这个办法效率完全可以接受,同时完全没有使用任何第三方的东西。
是具体实现时候要注意考虑前面提到的所有特殊情况。
下面来看下效果,为了展示各种情况,另外给个例子:
经过处理后,变成了:
有了这个功能,相信在作诸如自动缩略文处理时候就会轻松许多。(此刻让我想起了某些blog强制把html转成纯文本作略文的办法...)
最后,给出一个测试代码。喜欢的可以直接拿去用。当然,留下原始作者信息
http://www.csksoft.net/data/legacyftp/Products/code_and_lib/wise_cut_csk.zip