Здравствуйте уважаемые программисты! У меня есть проблема со скоростью функции preg_replace()
,
Когда я имел немного ценности (слова) в $patterns
а также $replacements
массивов, проблема была не с скорость поиска и замены в тексте от массивов, и когда номер значения в массивах увеличился на 1.000.000 тогда функция preg_replace()
был неоднократно замедлился. Как я могу найти и заменить в тексте, если у меня есть более 1 000 000 значений (слов) в массивах? Как сделать замену как быстро как можно? Может ли быть решение проблемы буферном или же кэшируются? Что посоветуете, как правильно мне поступить?
Вот пример моего массива:
$patterns =
array
(
0 => "/\bмувосокори\b/u",
1 => "/\bмунаггас\b/u",
2 => "/\bмангит\b/u",
3 => "/\bмангития\b/u",
4 => "/\bмунфачир\b/u",
5 => "/\bмунфачира\b/u",
6 => "/\bманфиатпарасти\b/u",
7 => "/\bманфиатчу\b/u",
8 => "/\bманфиатчуи\b/u",
9 => "/\bманфиатхох\b/u",
10 => "/\bманфи\b/u",
...........................
1000000 => "/\bмусби\b/u")
$replacements =
array
(
0 => "мувосокорӣ",
1 => "мунағғас",
2 => "манғит",
3 => "манғития",
4 => "мунфаҷир",
5 => "мунфаҷира",
6 => "манфиатпарастӣ",
7 => "манфиатҷӯ",
8 => "манфиатҷӯӣ",
9 => "манфиатхоҳ",
10 => "манфӣ",
.....................
1000000 => "мусбӣ");
$text = "мувосокори мунаггас мангит мангития мунфачир манфиатпарасти...";
$result = preg_replace($patterns, $replacements, $text);
Я использую эту функцию javascript в файле index.html:
<script>
function response(str) {
if (str.length == 0) {
document.getElementById("text").innerHTML = "";
return;
} else {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("text").innerHTML = this.responseText;
}
};
xmlhttp.open("GET", "response.php?request=" + str, true);
xmlhttp.send();
}
}
</script>
PHP файл response.php источник:
<?php
$patterns = array();
$replacements = array();
$request = $_REQUEST["request"];
$response = "";
if ($request !== "") {
$start = microtime(true);
$response = preg_replace($patterns, $replacements, $request);
$stop = microtime(true);
$time_replace = $stop - $start;
}
echo $response === "" ? "" : $response."<br>Time: $time_replace";
?>
Временная сложность вашего алгоритма примерно O (нм) где N количество слов в вашем массиве замены, и м количество слов в запросе.
Так как все шаблоны, кажется, ищут слова (\b
до и после) и не используйте какой-либо другой синтаксис регулярных выражений (только литеральные символы), вы получите лучшую производительность, разбив запрос на слова и просматривая их в ассоциативном массиве, без необходимости использования регулярных выражений вообще.
Поэтому определите ваши данные шаблона / замены как ассоциативный массив, например так:
$dict = array(
"мувосокори" => "мувосокорӣ",
"мунаггас" => "мунағғас",
"мангит" => "манғит",
"мангития" => "манғития",
"мунфачир" => "мунфаҷир",
"мунфачира" => "мунфаҷира",
"манфиатпарасти" => "манфиатпарастӣ",
"манфиатчу" => "манфиатҷӯ",
"манфиатчуи" => "манфиатҷӯӣ",
"манфиатхох" => "манфиатхоҳ",
"манфи" => "манфӣ",
...........................
"мусби" => "мусбӣ");
Тогда используйте preg_replace_callback
найти каждое слово в запросе и найти его в словаре выше:
$response = preg_replace_callback("/\pL+/u", function ($m) use ($dict) {
return isset($dict[$m[0]]) ? $dict[$m[0]] : $m[0];
}, $request);
Временная сложность будет линейной по отношению к количеству слов в запросе.
Если вам нужно сопоставить также любое изменение в заглавных буквах слов, то будет слишком много, чтобы сохранить любое такое изменение в словаре. Вместо этого вы должны хранить словарь строчными буквами, а затем использовать приведенный ниже код. Когда есть совпадение со словарем, он проверяет, что является заглавной буквой исходного слова, и применяет то же самое к слову замены:
$response = preg_replace_callback("/\pL+/u", function ($m) use ($dict) {
$word = mb_strtolower($m[0]);
if (isset($dict[$word])) {
$repl = $dict[$word];
// Check for some common ways of upper/lower case
// 1. all lower case
if ($word === $m[0]) return $repl;
// 2. all upper case
if (mb_strtoupper($word) === $m[0]) return mb_strtoupper($repl);
// 3. Only first letters are upper case
if (mb_convert_case($word, MB_CASE_TITLE) === $m[0]) return mb_convert_case($repl, MB_CASE_TITLE);
// Otherwise: check each character whether it should be upper or lower case
for ($i = 0, $len = mb_strlen($word); $i < $len; ++$i) {
$mixed[] = mb_substr($word, $i, 1) === mb_substr($m[0], $i, 1)
? mb_substr($repl, $i, 1)
: mb_strtoupper(mb_substr($repl, $i, 1));
}
return implode("", $mixed);
}
return $m[0]; // Nothing changes
}, $request);
Смотрите, это работает на repl.it
Вы можете использовать маленький кусочек кода для преобразования вашего текущего $ шаблоны а также $ замены Массивы в новую структуру данных, чтобы избежать этого, нужно делать это «вручную»:
foreach ($patterns as $i => $pattern) {
$dict[explode("\b", $pattern)[1]] = $replacements[$i];
}
Конечно, вам не следует включать это преобразование в ваш код, а просто запустить его один раз, чтобы создать новую структуру массива, а затем поместить этот массив в ваш код.
Других решений пока нет …