首先,我想提供一個非正則表達式方法,然後我會進入一些冗長的正則表達式考慮。
因爲你搜索 「針」 是整個單詞,你可以利用的str_word_count()
的魔法像這樣:
代碼:(Demo)
$targets=['apple','apples','pear','pears','strawberry','strawberries','grape','grapes']; // all lowercase
$input="Apples, pears, and strawberries are delicious. I probably favor the flavor of strawberries most. My brother's favorites are crabapples and grapes.";
$lowercase_input=strtolower($input); // eliminate case-sensitive issue
$words=str_word_count($lowercase_input,1); // split into array of words, permitting: ' and -
$unique_words=array_flip(array_flip($words)); // faster than array_unique()
$targeted_words=array_intersect($targets,$unique_words); // retain matches
$tags=implode(',',$targeted_words); // glue together with commas
echo $tags;
echo "\n\n";
// or as a one-liner
echo implode(',',array_intersect($targets,array_flip(array_flip(str_word_count(strtolower($input),1)))));
輸出:
apples,pears,strawberries,grapes
apples,pears,strawberries,grapes
現在關於正則表達式...
雖然matiaslauriti的回答可能會讓你a正確的結果,它提供很少的嘗試,以提供任何大的效益收益。在一個循環
不要使用preg_match()
時preg_match_all()
是專門設計來捕捉多次出現在一個單一的呼叫:
我會提出兩點。 (將在後面的答案提供的代碼)
凝聚你的邏輯模式儘可能...
比方說,你有一個這樣的輸入:
$input="Today I ate an apple, then a pear, then a strawberry. This is my article and it's about apples and pears. I like strawberries as well though.";
如果您使用該陣列的標籤:
$targets=['apple','apples','pear','pears','strawberry','strawberries','grape','grapes'];
,以產生像一個簡單的管道正則表達式模式:
/\b(?:apple|apples|pear|pears|strawberry|strawberries|grape|grapes)\b/i
這將需要的正則表達式引擎677步驟以匹配$input
所有的水果。 (Demo)
相反,如果你使用?
量詞這樣凝結在標籤元素:
\b(?:apples?|pears?|strawberry|strawberries|grapes?)\b
你的模式漲幅簡潔和效率,使相同的預期結果在短短501步。 (Demo)
生成此壓縮模式可以通過編程實現簡單關聯(包括複數形式和動詞連接)。
這裏是處理單數/複數關係的方法:
foreach($targets as $v){
if(substr($v,-1)=='s'){ // if tag ends in 's'
if(in_array(substr($v,0,-1),$targets)){ // if same words without trailing 's' exists in tag list
$condensed_targets[]=$v.'?'; // add '?' quantifier to end of tag
}else{
$condensed_targets[]=$v; // add tag that is not plural (e.g. 'dress')
}
}elseif(!in_array($v.'s',$targets)){ // if tag doesn't end in 's' and no regular plural form
$condensed_targets[]=$v; // add tag with irregular pluralization (e.g. 'strawberry')
}
}
echo '/\b(?:',implode('|',$condensed_targets),")\b/i\n";
// /\b(?:apples?|pears?|strawberry|strawberries|grapes?)\b/i
此技術將只處理最簡單的情況。您可以通過仔細檢查標籤列表並識別相關標籤並凝聚它們來真正提高性能。
執行我上面的方法來壓縮每個頁面加載時的管道模式會花費您的用戶加載時間。我非常強烈的建議是保存不斷增長的標籤的數據庫表,這些標籤存儲爲正則表達式標籤。當遇到/產生新標籤時,將它們分別自動添加到表格中。您應該定期查看〜5000關鍵字並找出可以合併而不會失去準確性的標籤。
它甚至可以幫助你維護數據庫表的邏輯,如果您對正則表達式模式的一列,而另一列這說明了什麼行的正則表達式模式包括CSV:
---------------------------------------------------------------
| Pattern | Tags |
---------------------------------------------------------------
| apples? | apple,apples |
---------------------------------------------------------------
| walk(?:s|er|ed|ing)? | walk,walks,walker,walked,walking |
---------------------------------------------------------------
| strawberry | strawberry |
---------------------------------------------------------------
| strawberries | strawberries |
---------------------------------------------------------------
爲了提高效率,
---------------------------------------------------------------
| strawberr(?:y|ies) | strawberry,strawberries |
---------------------------------------------------------------
有了這樣一個簡單的改進,從59,如果你只檢查$input
這兩個標籤,操作步驟需要滴:您可以通過合併這樣的草莓和草莓行更新表數據至。
因爲您正在處理> 5000個標籤,所以性能改進將非常明顯。這種細化最好在人的層面上處理,但是你可以使用一些編程技術來識別共享內部子字符串的標記。
當你想使用你的Pattern列值時,只需從數據庫中拉出它們,將它們連接在一起,然後將它們放入preg_match_all()
。
*請記住,在將標籤壓縮成單個模式時應使用非捕獲組,因爲我遵循的代碼將通過避免捕獲組來減少內存使用量。
代碼(Demo Link):
$input="Today I ate an apple, then a pear, then a strawberry. This is my article and it's about apples and pears. I like strawberries as well though.";
$targets=['apple','apples','pear','pears','strawberry','strawberries','grape','grapes'];
//echo '/\b(?:',implode('|',$targets),")\b/i\n";
// condense singulars & plurals forms using ? quantifier
foreach($targets as $v){
if(substr($v,-1)=='s'){ // if tag ends in 's'
if(in_array(substr($v,0,-1),$targets)){ // if same words without trailing 's' exists in tag list
$condensed_targets[]=$v.'?'; // add '?' quantifier to end of tag
}else{
$condensed_targets[]=$v; // add tag that is not plural (e.g. 'dress')
}
}elseif(!in_array($v.'s',$targets)){ // if tag doesn't end in 's' and no regular plural form
$condensed_targets[]=$v; // add tag with irregular pluralization (e.g. 'strawberry')
}
}
echo '/\b(?:',implode('|',$condensed_targets),")\b/i\n\n";
// use preg_match_all and call it just once without looping!
$tags=preg_match_all("/\b(?:".implode('|',$condensed_targets).")\b/i",$input,$out)?$out[0]:null;
echo "Found tags: ";
var_export($tags);
輸出:
/\ B(:????蘋果|梨|草莓|草莓|葡萄)\ B/I
已找到標籤array(0 =>'apple',1 =>'pear',2 => 'strawberry',3 =>'apples',4 =>'pear',5 =>'strawberries', )
......如果你已經設法閱讀了這篇文章,你可能會遇到類似於OP的問題,並且你想繼續前進而沒有遺憾/錯誤。請參閱my related Code Review post瞭解更多關於附帶案例考慮事項和方法邏輯的信息。
它爲什麼會運行250萬次?它只對每個$ target檢查$ a,它只會運行count($ targets)次。 – Devon
如果您的文章有400-1000字,那麼您應該首先反駁。沒有在文章中找到標籤,而是從標籤中的文章中找到單詞。 (這將是更有效的5-10倍)。有了這個解決方案,你也可以先過濾短的單詞(a,an,the,is ....)並且不要搜索它們。 –
由於您只在找到目標單詞時才添加條目到'$ b'中,您可以使用'echo implode(',',$ b);'來顯示您找到的單詞。 – roberto06