有没有比php preg_match更快、更简单的替代方案?

3
我正在使用cakephp 1.3,我有一个文本框,用户可以在其中提交文章。当提交时,我希望查找文章中的某些关键词,并将相应的标签添加到文章中。
我考虑使用preg_match,但preg_match模式必须是字符串。所以我不得不遍历一个数组(大型)。
有没有更简单的方法将关键字数组插入模式中?
感谢您的所有帮助。
谢谢。
6个回答

3

我建议将您的关键字数组视为哈希表。将文章文本转换为小写,按空格分割,然后循环遍历每个单词。如果单词存在于哈希表中,则将其推送到一个新的数组中,并跟踪其出现次数。

我进行了一个快速基准测试,比较了在这种情况下使用正则表达式和哈希表的效果。使用正则表达式运行1000次需要17秒,而使用哈希表运行1000次只需要0.4秒。这应该是一个O(n+m)的过程。

$keywords = array("computer", "dog", "sandwich");
$article = "This is a test using your computer when your dog is being a dog";
$arr = explode(" ", strtolower($article));
$tracker = array();

foreach($arr as $word){
    if(in_array($word, $keywords)){
        if(isset($tracker[$word]))
            $tracker[$word]++;
        else 
            $tracker[$word] = 1;
    }
}
$tracker 数组将输出: "computer" => 1, "dog" => 2。然后你可以进行处理来决定使用哪些标签。或者,如果你不关心关键词出现的次数,可以跳过 tracker 部分,当关键词出现时添加标签。
编辑:关键词数组可能需要是一个倒排索引数组以确保最快的查找。我不确定 in_array() 的工作原理,但如果它进行搜索,则这不会像应该一样快。倒排索引数组应该长这样:
array("computer" => 1, "dog" => 1, "sandwich" => 1); // "1" can be any value

那么你可以使用isset($keywords[$word])来检查单词是否匹配关键字,而不是使用in_array(),这样可以得到O(1)的复杂度。但其他人可能能更好地为我解释这个问题。


谢谢您的快速回复。当文章中没有XHTML标签时,它可以正常工作。但是我的文章中有XHTML标签。是否有解决方法? - Josh R
嗯,那么正则表达式可能无法完全避免。在将文章字符串转换为小写之前,您可以对其应用strip_tags()。这应该可以处理所有XHTML标记。但是速度可能会受到轻微影响。 - Nick
这几乎是我使用array_count_values()和str_word_count()完成的相同操作.. :) @JoshR你需要去除XHTML标签吗?因为你没有提到它。 - redShadow

2

如果你不需要正则表达式的强大功能,你应该使用strpos()函数。

你仍然需要遍历词汇数组,但是strpospreg_match要快得多。


2

如果你想从数组中查找多个单词,则将该数组组合成正则表达式:

 $regex_array = implode("|", array_map("preg_escape", $array));
 preg_match_all("/($regex_array)/", $src, $tags);

这会将你的数组转换为/(word|word|word|word|word|...)/。数组地图和preg_escape部分是可选的,只有在$array可能包含特殊字符时才需要。
对于这种情况,请避免使用strpos和循环。 preg_match用于搜索替代方案更快。

2
当然,你可以尝试使用一个单一的正则表达式匹配所有关键字,例如/word1|word2|word3/,但我不确定这是否符合你的要求。而且我认为它会非常沉重和消耗资源。
相反,你可以尝试另一种方法,比如将文本分割成单词,并检查这些单词是否有趣。我会使用str_word_count(),类似这样:
$text = 'this is my string containing some words, some of the words in this string are duplicated, some others are not.';
$words_freq = array_count_values(str_word_count($text, 1));

将文本拆分为单词并计算出现次数。然后您可以使用 in_array($keyword, $words_freq)array_intersect(array_keys($words_freq), $my_keywords) 进行检查。

如果您对关键字的大小写不敏感,可以在进行单词拆分之前使用 strtolower() 对整个文本进行转换。

当然,确定最佳方法的唯一方法是设置一些测试,通过运行各种搜索函数来测量执行时间和资源使用情况(尝试使用 microtime(TRUE)memory_get_peak_usage() 进行基准测试)。

编辑:我清理了一下代码并添加了一个缺失的分号 :)


0

strtr()

如果给定两个参数,则第二个参数应该是一个数组,形式为array('from' => 'to', ...)。返回值是一个字符串,其中所有数组键的出现都已被相应的值替换。最长的键将首先尝试。一旦替换了子字符串,它的新值就不会再次搜索。


strtr很好,但Josh不是在寻找替换,而是在寻找搜索。 - Raphael Michel
哦,我以为他想用链接替换关键词或其他什么东西。 - AndreKR

0
手动添加标签?就像我们在 SO 上添加标签一样。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接