Preg_replace regex在PHP中给出了意想不到的空结果


Preg_replace regex in PHP gives unexpected empty result

我使用正则表达式将字符串中的所有电子邮件地址替换为一个漂亮的<a>,使它们可单击。这是完美的,除了当有两个最小长度的单词和它们之间的破折号在电子邮件地址前面的情况。只有这样我才能得到一个空字符串作为结果。

<?php
$search = '#(^|[ 'n'r't])(([a-z0-9'-_]+('.?))+@([a-z0-9'-]+('.?))+[a-z]{2,5})#si';
$replace = '''1<a href="mailto:''2">''2</a>';
$string = "tttteeee-sssstttt mail@test.nl";
echo preg_replace($search, $replace, $string);
// Output: "" (empty)
$string = "te-st mail@test.nl";
echo preg_replace($search, $replace, $string);
// Output: "te-st <a href="mailto:mail@test.nl">mail@test.nl</a>" (as expected)
$string = "mail@test.nl tttteeee-sssstttt";
echo preg_replace($search, $replace, $string);
// Output: "<a href="mailto:mail@test.nl">mail@test.nl</a> tttteeee-sssstttt" (as expected)
?>
我什么都试过了,但就是找不到问题。一种解决方案是删除正则表达式中的第一个破折号(在@符号之前),但这样在@之前有破折号的电子邮件地址就不会被突出显示。

OK,最小用例:#([a-z-]+'.?)+@#,它达到了回溯限制(使用preg_last_error()),它不能确定在哪里放东西,因为'.是可选的,确定是使用内部还是外部+是很多工作。pcre.backtrack_limit的默认限制为100000行不通,可以将其设置为1000000。

要解决这个问题,让解析器更容易:第一个(([a-z0-9'-_]+('.?))+应该变成:([a-z0-9'-_]+('.[a-z0-9'-_]+)*),这在内部解决起来容易得多。作为奖励,除了公认的答案之外,这仍然不允许连续点。

试试用这个来代替你的搜索字符串:

$search = '#(^|'b)([A-Z0-9_'-.]+@[A-Z0-9_'-.]+'.[A-Z]{2,5})($|'b)#i';