Более быстрая и простая альтернатива php preg_match - PullRequest
3 голосов
/ 30 ноября 2010

Я использую cakephp 1.3, и у меня есть textarea, где пользователи отправляют статьи. При отправке я хочу просмотреть статью для определенных ключевых слов и добавить соответствующие теги в статью.

Я думал о preg_match, но шаблон preg_match должен быть строкой. Так что мне пришлось бы перебрать массив (большой).

Есть ли более простой способ подключить массив ключевых слов для шаблона.

Я ценю вашу помощь.

Спасибо.

Ответы [ 6 ]

3 голосов
/ 30 ноября 2010

Я предлагаю рассматривать ваш массив ключевых слов как хеш-таблицу. Строчный текст статьи, разнесение по пробелам, затем цикл по каждому слову в разобранном массиве. Если слово существует в вашей хеш-таблице, переместите его в новый массив, отслеживая количество раз, которое оно было просмотрено.

В этом сценарии я провел быстрый сравнительный тест, сравнивающий регулярное выражение с хеш-таблицами. Чтобы запустить его с регулярным выражением 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. Затем вы можете выполнить процесс, чтобы решить, какие теги использовать. Или, если вам не важно, сколько раз ключевое слово появляется, вы можете пропустить часть трекера и добавить теги в качестве ключевых слов.

РЕДАКТИРОВАТЬ: Может потребоваться, чтобы массив ключевых слов был массивом инвертированных индексов, чтобы обеспечить быстрый поиск. Я не уверен, как работает in_array (), но если он ищет, то это не так быстро, как должно быть. Инвертированный индексный массив будет выглядеть как

array("computer" => 1, "dog" => 1, "sandwich" => 1); // "1" can be any value

Тогда вы должны выполнить isset ($words [$ word]), чтобы проверить, соответствует ли слово ключевому слову, а не in_array (), что должно дать вам O (1). Кто-то еще может объяснить это мне.

2 голосов
/ 30 ноября 2010

Если вы хотите найти несколько слов в массиве, то объедините указанный массив в регулярное выражение:

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

Это преобразует ваш массив в /(word|word|word|word|word|...)/. Часть arrray_map и preg_escape является необязательной, она необходима только в том случае, если $ array может содержать специальные символы.

Избегайте strpos и петель для этого случая. preg_match быстрее для поиска альтернатив.

2 голосов
/ 30 ноября 2010

Конечно, вы можете попробовать сопоставить все ключевые слова, используя одно регулярное выражение, например /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() для сравнения).

РЕДАКТИРОВАТЬ: Я немного почистил код и добавил пропущенную точку с запятой:)

1 голос
/ 30 ноября 2010

Если вам не нужна мощь регулярных выражений, вы должны просто использовать strpos().

Вам все равно нужно будет пройтись по массиву слов, но strpos намного, намного быстрее, чем preg_match.

0 голосов
/ 30 ноября 2010

Добавить теги вручную? Так же, как мы добавляем теги здесь, в SO.

0 голосов
/ 30 ноября 2010

strtr()

Если дано два аргумента, второй должен быть массив в виде массив ('from' => 'to', ...). Возврат значение представляет собой строку, где все вхождения ключей массива были заменены соответствующими ценности. Самые длинные ключи будут опробованы первый. После подстроки заменено, его новое значение не будет поиск снова.

...