Замена (добавление) слов с учетом регистра из массивов

#php #arrays #regex

#php #массивы #регулярное выражение

Вопрос:

Я новичок в php и особенно в регулярных выражениях. Моя цель — автоматически обогащать текстовые сообщения подсказками для «ключевых слов», которые перечислены в массивах.

Так далеко я зашел.

 $pattern = array("/bexplanationsb/i",
            "/btargetb/i", 
            "/bhintsb/i",
            "/bhintb/i",
);

$replacement = array("explanations <i>(Erklärungen)</i>",
            "target <i>Ziel</i>", 
            "hints <i>Hinsweise</i>",
            "hint <i>Hinweis</i>",
);

$string = "Target is to add some explanations (hints) from an array to 
this text. I am thankful for every hint.";

echo preg_replace($pattern, $replacement, $string);
 

ВОЗВРАТ:

 target <i>Ziel</i> is to add some explanations <i>(Erklärungen)</i> (hints <i>Hinsweise</i>) from an array to this text. I am thankful for every hint <i>Hinweis</i>
 

1) В общем, мне интересно, есть ли более элегантные решения (в конечном итоге без замены исходного слова)?
В более позднем состоянии массивы будут содержать более 1000 элементов… и исходят из mariadb.

2) Как я могу добиться, чтобы слово «Targets» обрабатывалось с учетом регистра? (без дублирования длины моих массивов).

Извините за мой английский и заранее большое спасибо.

Комментарии:

1. Заключайте поисковые слова в круглые скобки в шаблонах регулярных выражений и используйте обратные ссылки при заменах.

Ответ №1:

Если вы хотите увеличить размер своего массива и если текст может быть немного длинным, обработка всего текста (один раз на слово) не является надежным способом. Кроме того, при большом массиве ненадежно создавать гигантское чередование со всеми словами. Но если вы сохраните все переводы в ассоциативном массиве и разделите текст по границам слов, вы можете сделать это за один проход:

 // Translation array with all keys lowercase
$trans = [ 'explanations' => 'Erklärungen',
           'target' => 'Ziel',
           'hints' => 'Hinsweise',
           'hint' => 'Hinweis'
];

$parts = preg_split('~b~', $text);

$partsLength = count($parts);

// All words are in the odd indexes
for ($i=1; $i<$partsLength; $i =2) {
    $lcWord = strtolower($parts[$i]);

    if (isset($trans[$lcWord]))
        $parts[$i] .= ' <i>(' . $trans[$lcWord] . ')</i>';
}

$result = implode('', $parts);
 

На самом деле ограничение здесь заключается в том, что вы не можете использовать ключ, который содержит границу слова (например, если вы хотите перевести целое выражение из нескольких слов), но если вы хотите справиться с этим случаем, вы можете использовать preg_match_all вместо preg_split и создать шаблон, который проверяет эти особые случаи раньше,что-то вроде:

 preg_match_all('~mushroom pieb|w |W*~iS', $text, $m);

$parts = amp;$m[0];
$partsLength = count($parts);

$i = 1 ^ preg_match('~^w~', $parts[0]);

for (; $i<$partsLength; $i =2) {

...
 

(если у вас много исключений (слишком много), возможны другие стратегии.)

Комментарии:

1. Большое спасибо. Как новичку мне понадобилось несколько часов, чтобы пройти эту строку за строкой. И после тестирования в учебных целях я все еще задаюсь вопросом о том, насколько стабильно это работает, особенно если я использую ввод с двойным пробелом и т.д. И хотя мои текущие навыки недостаточно сложны, чтобы полностью решить проблему производительности, мои дальнейшие исследования стали значительно более конкретным направлением.

2. @FriedrichSiever: проблема с производительностью проста, использование preg_replace (или str_replace ) с массивом шаблонов включает неявный цикл, подлежащая строка полностью обрабатывается для каждого элемента массива (короче говоря, если у вас 1000 слов, строка анализируется 1000 раз, и эта строка будет расти после каждой замены). Если вы используете мое решение, строка анализируется только один раз для построения массива частей, тогда вам нужно проверить только половину частей (слов), если в массиве замены есть ключ (построение массива замены таким образом позволяет избежать использования in_array )

3. @FriedrichSiever: другая проблема, рассмотрим текст: "a child is playing." и массив ['child' => 'Kind', 'kind' => 'Art'] , с preg_replace str_replace помощью метода or вы получаете: "a child <i>(Kind <i>(Art)</i>)</i> is playing." . Я надеюсь, что у вас нет текста со словом "fat" .

4. Еще раз благодарю вас за ваши добрые и ценные объяснения и примеры. Они очень поучительны для меня.

Ответ №2:

Заключайте поисковые слова в круглые скобки в шаблонах регулярных выражений и используйте обратные ссылки при заменах.

Посмотрите эту демонстрацию PHP:

 $pattern = array("/b(explanations)b/i", "/b(target)b/i", "/b(hints)b/i", "/b(hint)b/i", ); 
$replacement = array('$1 <i>(Erklärungen)</i>', '$1 <i>Ziel</i>', '$1 <i>Hinsweise</i>', '$1 <i>Hinweis</i>', ); 
$string = "Target is to add some explanations (hints) from an array to this text. I am thankful for every hint."; 
echo preg_replace($pattern, $replacement, $string);
 

Таким образом, вы замените найденные слова фактическим регистром, используемым в тексте.

Обратите внимание, что очень важно убедиться, что шаблоны идут в порядке убывания, причем более длинные шаблоны идут перед более короткими (сначала Targets , затем Target и т.д.)

Комментарии:

1. Вау. Большое вам спасибо. Это точно решает мою проблему и дает мне правильное направление для дальнейших исследований и исследований в этой предметной области. Хотя регулярное выражение кажется сложным для изучения, оно действительно мощное. Еще раз спасибо за вашу демонстрацию.

2. Наконец-то сделано. Что касается моего нового состояния, здесь мне не разрешили на момент вашего ответа … .