请问php中url替换的问题 正则也可以?
html start
请从以下推荐分类中选择合适的分类:<br>电脑/网络 -> <a href="link1" alt="程序设计">程序设计</a><br>
电脑/网络 -> 操作系统/系统故障<br>
<br>
如果没有合适的推荐分类,建议您更改分类,有助于获得准确解答 <br>
<img src=asdasda.jpg alt="test" title="你好"><br>
您还可以输入1381字<br>
test<br>
输入内容已经达到长度限制<br>
html end
匹配的词为
array(
1 => "test",
2 => "你好",
3 => "程序" ,
4 => "分类"
);
要求除了 img及a标签 外所有的都进行匹配 其中url地址为 数组的key
例如 匹配的 test 变成 <a href="1.html" title="test">test</a>
每个词只匹配一次
结果如下:
html start
请从以下推荐<a href="4.html" title="分类">分类</a>中选择合适的分类:<br>电脑/网络 -> <a href="link1" alt="程序设计">程序设计</a><br>
电脑/网络 -> 操作系统/系统故障<br>
<br>
如果没有合适的推荐分类,建议您更改分类,有助于获得准确解答 <br>
<img src=asdasda.jpg alt="test" title="你好"><br>
您还可以输入1381字<br>
<a href="1.html" title="test">test</a><br>
输入内容已经达到长度限制<br>
html end 展开
首先赞扬一下楼主的提问方式:
1. 对问题描述得很清晰;
2. 给出了匹配的范例文本;
3. 指定了正则式所在的语言。
这才应该是规范合理的提问。
言归正传,先根据问题,写出正则,然后落实到PHP语言。如果对分析过程不感兴趣,请直接pagedown看答案。
1. 基础
需要匹配的关键词为:test,你好,程序,分类。
正则表达式:/test|你好|程序|分类/i
(i是表示大小写不敏感模式。如果指定匹配小写的test,则可以去掉i)
2. 限制条件
a. 不在标签内部,例如<img src=asdasda.jpg alt="test" title="你好">,里面虽然有test和你好,但这不满足要求;
b. 本身无链接。例如,<a href="link1" alt="程序设计">程序设计</a>中,>程序设计<中的程序由于处于<a..>程序设计</a>之间,同样不满足要求。
根据以上限制,写出纯粹的正则式(并不能直接用在php中):
(?<!<(?:a|img)[^<>]*)(test|你好|程序|分类)(?![^<>]*</a>)
它表示,(test|你好|程序|分类)的紧临左侧不能出现<a..标签,或者<img标签;紧临右侧不能出现</a>标签。
“紧临”二字至关重要,它将无直接关系的<>都忽略掉,才确保条件的正确执行。怎样实现这一点呢?我使用的是[^<>]*。
正则式的部分这里就交待清楚了。下面将正则式应用到PHP中。
3. php正则式
php使用的正则表达式是PCRE的,它不支持在lookbehind里使用无限量词,即(?<!<(?:a|img)[^<>]*)部分不被PCRE支持。没关系,可以修改之。
(?<!(?:<a|<img))([^<>]*)(test|你好|程序|分类)(?![^<>]*(?:>|</a>))
与2中的正则式相比,前者$1即为所求,其余部分只匹配,不消耗字符;而后者$2为所求,还需要对$1进行处理。
4. php细节
我使用preg_replace_callback函数,以便自定义替换过程。代码如下,随手注释:
<?php
//此处使用heredoc语法,以便支持复杂的长文本。
$x=<<<EOT
请从以下推荐分类中选择合适的分类:<br>电脑/网络 -> <a href="link1" alt="程序设计">程序设计</a><br>
电脑/网络 -> 操作系统/系统故障<br>
<br>
如果没有合适的推荐分类,建议您更改分类,有助于获得准确解答 <br>
<img src=asdasda.jpg alt="test" title="你好"><br>
您还可以输入1381字<br>
test<br>
输入内容已经达到长度限制<br>
EOT;
//关键词数组
$a=array(
1 => "test",
2 => "你好",
3 => "程序" ,
4 => "分类"
);
//记号数组,用于记录哪些匹配是已经替换了的。因为楼主指定“每个词只匹配一次”。
$index=array();
function compute_replacement($groups) {
global $a;
global $index;
if ($index[$groups[2]])
{
//如果该关键词已经被处理过,那么直接返回整个字串($groups[0]),不作处理。
return $groups[0];
}
//查询该关键词在数组中的位置,确定序号,以便生成href="1.html"之类链接地址。
$in=array_search($groups[2],$a);
//将处理过的关键词标记为已处理,避免重复。
$index[$groups[2]]=1;
//返回处理好的文本。
return "$groups[1]<a href=\"$in.html\" title=\"$groups[2]\">$groups[2]</a>";
}
$r= preg_replace_callback('%(?<!<)([^<>]*)(test|你好|程序|分类)(?![^<>]*(?:>|</a>))%s', compute_replacement, $x);
echo $r;
?>
输出的结果为:
请从以下推荐分类中选择合适的<a href="4.html" title="分类">分类</a>:<br>电脑/网络 -> <a href="link1" alt="程序设计">程序设计</a><br>
电脑/网络 -> 操作系统/系统故障<br>
<br>
如果没有合适的推荐分类,建议您更改分类,有助于获得准确解答 <br>
<img src="asdasda.jpg" alt="test" title="你好"><br>
您还可以输入1381字<br>
<a href="1.html" title="test">test</a><br>
输入内容已经达到长度限制<br></body>
注:这里的格式不好,建议去“参考资料”所指向的地址察看更易读版本的解答。
正则表达式论坛:http://regex.me
正则表达式博客:http://iregex.org
附:匹配图。高亮部分为匹配结果。
参考资料: http://regex.me/thread-113-post-317.html#pid317