regex — система тегов PHP с preg_match и foreach

Я пытаюсь создать эту систему тегов для моего сайта, где она проверяет написанную статью (может быть 400-1000 слов), для конкретных слов и создает строку со всеми найденными ключевыми словами из массива.

Тот, который я сделал, работает нормально, но есть некоторые проблемы, которые я хотел бы исправить.

$a = "This is my article and it's about apples and pears. I like strawberries as well though.";

$targets = array('apple', 'apples','pear','pears','strawberry','strawberries','grape','grapes');
foreach($targets as $t)
{
if (preg_match("/\b" . $t . "\b/i", $a)) {
$b[] = $t;
}
}
echo $b[0].",".$b[1].",".$b[2].",".$b[3];
$tags = $b[0].",".$b[1].",".$b[2].",".$b[3];

Прежде всего, я хотел бы знать, если есть какой-то способ, я могу сделать это более эффективным. У меня есть база данных с около 5000 ключевых слов и расширяется с каждым днем.

А вы видите, я не знаю, как получить ВСЕ спички. Я пишу $ b [0], $ b [1] и т. Д.

Я хотел бы, чтобы он просто делал строку со ВСЕМИ совпадениями — но только 1 раз за матч. Если яблоки упоминаются 5 раз, то в строке должно быть только 1.

А сказал — это работает. Но я не чувствую, что это лучшее решение.

РЕДАКТИРОВАТЬ:

Я сейчас пытаюсь это, но я не могу заставить его работать на всех.

$a = "This is my article and it's about apples and pears. I like strawberries as well though.";

$targets = array('apple', 'apples','pear','pears','strawberry','strawberries','grape','grapes');
$targets = implode('|', $targets);
$b = [];
preg_match("/\b(" . $targets . ")\b/i", $a, $b);

echo $b;

0

Решение

preg_match уже сохраняет спички. Так:

int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )

Параметр 3 уже сохраняет совпадения, измените это:

if (preg_match("/\b" . $t . "\b/i", $a)) {
$b[] = $t;
}

К этому:

$matches = [];
preg_match("/\b" . $t . "\b/i", $a, $matches);
$b = array_merge($b, $matches);

Но, если вы сравниваете непосредственно слово, документация рекомендует использовать strpos().

Совет
Не используйте preg_match (), если вы хотите проверить, содержится ли одна строка в другой. Вместо этого используйте strpos (), так как это будет быстрее.


РЕДАКТИРОВАТЬ

Вы могли бы улучшить (по производительности) свой код, если вы все еще хотите использовать preg_match сделав это, замените это:

$targets = array('apple', 'apples','pear','pears','strawberry','strawberries','grape','grapes');
foreach($targets as $t)
{
if (preg_match("/\b" . $t . "\b/i", $a)) {
$b[] = $t;
}
}

С этим:

$targets = array('apple', 'apples','pear','pears','strawberry','strawberries','grape','grapes');
$targets = implode('|', $targets);

preg_match("/\b(" . $t . ")\b/i", $a, $matches);

Здесь вы присоединяетесь ко всем своим $targets с | (труба), так что ваше регулярное выражение выглядит так: (target1|target2|target3|targetN) так что вы делаете только один поиск, а не тот foreach.

0

Другие решения

Во-первых, я хотел бы предоставить метод без регулярных выражений, а затем я перейду к некоторым многословным рассуждениям о регулярных выражениях.

Поскольку ваш поиск «иглы» являются целыми словами, вы можете использовать магия из str_word_count() вот так:

Код: (демонстрация)

$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

Теперь о регулярном выражении …

В то время как ответ Матиаслаурити может дать вам правильный результат, он делает очень мало попыток обеспечить какой-либо большой выигрыш в эффективности.

Я сделаю два замечания:

  1. Не использовать preg_match() в цикле, когда preg_match_all() был специально разработан для захвата нескольких вхождений за один звонок. (код будет предоставлен позже в ответе)

  2. Максимально сузьте логику вашего паттерна …

Допустим, у вас есть такой вклад:

$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, (демонстрация)

Напротив, если вы уплотняете элементы тега, используя ? квантификатор вот так:

\b(?:apples?|pears?|strawberry|strawberries|grapes?)\b

Ваша модель получает краткость и эффективность, давая тот же ожидаемый результат всего за 501 шаг. (демонстрация)

Генерация этого сжатого паттерна может быть выполнена программно для простых ассоциаций (включая множественное число и спряжения глаголов).

Вот метод для обработки отношений единственное / множественное число:

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           |
---------------------------------------------------------------

С таким простым улучшением, если вы только проверяете $input для этих двух тегов необходимые шаги отбрасываются из 59 в 40.

Поскольку вы имеете дело с> 5000 тегами, улучшение производительности будет очень заметным. Этот вид уточнения лучше всего обрабатывать на человеческом уровне, но вы можете использовать некоторые программные приемы для определения тегов, которые разделяют внутреннюю подстроку.

Если вы хотите использовать значения столбцов Pattern, просто извлеките их из базы данных, соедините их вместе и поместите в них 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'];
//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);

Выход:

/ \ Б (??? Яблоки | груши | клубника | клубника | виноград) \ б / я

Найдено тегов: массив (0 => ‘яблоко’, 1 => ‘груша’, 2 =>
«клубника», 3 => «яблоки», 4 => «груши», 5 => «клубника»,
)


…если вам удалось прочитать это далеко внизу моего поста, у вас, вероятно, есть проблема, подобная ОП, и вы хотите двигаться вперед без сожалений / ошибок. Пожалуйста, перейдите к мой связанный пост обзора кода для получения дополнительной информации о дополнительных случаях и логике метода.

1

По вопросам рекламы [email protected]