【发布时间】:2014-03-26 07:22:30
【问题描述】:
我需要从字符串的开头和结尾修剪单词。问题是,有时这些词可以缩写为 ie。只有前三个字母(后跟点)。
我努力寻找合适的正则表达式。基本上我需要聊天三个或更多初始字符直到替换长度,但我找不到正则表达式,它将匹配可变长度并保持字符顺序。
例如,如果我需要从句子'insur. companies are rich' 中修剪'insurance',那么我会想到模式\^[insurance]{3,9}\,但是这种模式也会捕捉到像'sensace' 这样的词,因为字符的顺序(以及它们的出现) 在[] 中对于正则表达式并不重要。
另外,在字符串的末尾,我需要删除从 beginig 缩写的序列号 - 比如 'XK-25F14' 有时显示为 '25F14'。所以我决定纯粹用逐个字符来比较。
因此我以下面的 php 函数结束
function trimWords($s, $dirt, $case_insensitive = false, $reverse = true)
{
$pos = 0;
$func = $case_insensitive ? 'strncasecmp' : 'strncmp';
// Get number of initial characters, that match in both strings
while ($func($s, $dirt, $pos + 1) === 0)
$pos++;
// If more than 2 initial characters match, then remove the match
if ($pos > 2)
$s = substr($s, $pos);
// Reverse $s and $dirt so it will trim from the end of string
$s = strrev($s);
if ($reverse)
return trimWords($s, strrev($dirt), $case_insensitive, false);
// After second run return back-reversed string
return trim($s, ' .-');
}
我对这个功能很满意,但它有一个缺点。它只修剪一次出现的单词。如何让它修剪更多的出现,即从'Insurance insur. companies'中删除'insurance '。
我也很好奇,它真的不存在这样的正则表达式,它会匹配可变长度并尊重模式中的字符顺序?
最终解决方案
感谢 mrhobo,我以基于正则表达式的函数结束。此功能可以轻松改进,并且对于此任务也应该是最有效的。
我已经修改了我之前的函数,它比正则表达式快两倍,但它每次运行只能删除一个单词,所以要能够从开头和结尾删除单词,它必须自己运行两次,性能是与正则表达式相同,并且要删除多次出现的单词,它必须多次运行自己,然后会越来越慢。
最终的功能是这样的。
function trimWords($string, $word, $case_insensitive = false, $min_abbrv = 3)
{
$exc = substr($word, $min_abbrv);
$pat = null;
$i = strlen($exc);
while ($i--)
$pat = '(?>'.preg_quote($exc[$i], '#').$pat.')?';
$pat = substr($word, 0, $min_abbrv).$pat;
$pat = '#(?<begin>^)?(?:\W*\b'.$pat.'\b\W*)+(?(begin)|$)#';
if ($case_insensitive)
$pat .= 'i';
return preg_replace($pat, '', $string);
}
注意:使用此功能,无论缩写是否以点结尾,它都会消除任何较短的单词形式,并删除单词周围的所有非单词字符。
编辑:我刚刚尝试创建像 insu(r|ra|ran|ranc|rance) 这样的替换模式,使用原子组的函数速度快了约 30%,而且如果单词越长,它可能效率更高。
【问题讨论】:
-
怎么样:insur(ance|\.)
-
@mrhobo:在这种情况下它可以工作,但如果有人将它缩写为
'ins.'或'insura.'。我不想构建像/ins((u)|(ur)|(ura)|(uran)|(urance))\.?/这样的搜索模式,虽然它可以是一种方式,但对我来说似乎相当复杂和低效。 -
困难的一个。我想出了这个: insu(?>r|\.)(?>a|\.)?(?>r|\.)?(?>n|\.)?(?>c|\.) ?(?>e|\.)?
-
@mrhobo: 很好地尝试前瞻,但是当你开始在模式中使用元字符
?时,字符的顺序也可能被破坏。