木匣子

Web/Game/Programming/Life etc.

正则替换冲突

需要将一篇日志中的超链接自动转换成 A-tag,可以使用正则表达式查询URL并替换成<a href="URL">URL</a>

同样的,要把图像链接自动转换成 IMG-tag,可以使用正则表达查询IMG并替换成<img src="IMG"/>

但是如果希望将文章中的超链接和图片分别变成 A-tag 和 IMG-tag,能否分步执行上面两个过程?显然是不行的,因为图像链接是超链接的一个子集。如果先将URL替换成<a href="URL">URL</a>;再将IMG并替换成<img src="IMG"/>;可能会发生一部分的<a href="URL">URL</a>变成了<a href="<img src="IMG"/>"><img src="IMG"/></a>,出现了冲突。

如何解决这个问题呢?

这类处理大量被使用在论坛或博客中,所以我翻看了 wordpress 源码,关于如何处理 shortcode 的部分。最终我发现了解决问题的关键函数:preg_replace_callback()

preg_replace_callback — 执行一个正则表达式搜索并且使用一个回调进行替换
这个函数的行为除了 可以指定一个 callback 替代 replacement 进行替换 字符串的计算,其他方面等同于 preg_replace()。
http://hk1.php.net/preg_replace_callback

preg_replace_callback() 很巧妙的用回调函数解决了这个问题。实际上我们需要把一个有交集的匹配分类,但对每一个匹配只做一次替换。于是我们可以先构造这两个有交集的匹配的超集,对于超链接和图像链接而言,这个超集就是超链接。然后将这个超集匹配到的子串交给回调函数处理。在回调函数中完成分类,最终确定最后替换的结果。

这里我用 javascript 写一个简单的例子供参考,匹配URL的正则来自这里,匹配IMG的正则来自这里,代码如下:

var url = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/g;

var img = /(?:([^:\/?#]+):)?(?:\/\/([^\/?#]*))?([^?#]*\.(?:jpg|gif|png))(?:\?([^#]*))?(?:#(.*))?/;

var post = "这个网页 http://example.com/ 里面有一张很漂亮的图片:http://example.com/img.jpg";

var result = post.replace(url, function(str) {
	if (img.test(str))
		return "<img src='" + str + "'/>";
	else
		return "<a href='" + str + "'>" + str + "</a>";
});

运行结果 result 为:

这个网页 <a href='http://example.com/'>http://example.com/</a> 里面有一张很漂亮的图片:<img src='http://example.com/img.jpg'/>

javascript 的 String.replace() 同样允许使用回调函数来处理替换,可见回调函数很好地解决了该问题。

问题来源: 求一段正则