如何在 $patterns 和 $replacements 数组中使用具有大量 (1000000) 个值的函数 preg_replace?

How to work with a function preg_replace with a large number (1000000) of values in $patterns and $replacements arrays?

亲爱的程序员,您好!我在函数 preg_replace().[=20] 的速度方面遇到 问题 =]

当我在 $patterns$replacements 数组中有 little 值(words)时,问题不是用 speed 从数组中搜索和替换 in text,并且当数组 中的值的数量增加时1.000.000 然后函数 preg_replace() 反复 变慢 。如果数组中的值(单词)超过 1,000,000 个,我该如何在文本中进行搜索和替换?如何尽可能快速替换?问题的解决方法是buffered还是cached?有什么建议,如何正确地行动?

这是我的数组示例:

$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);

I use this javascript function in index.html file:

<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 file response.php source:

<?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(nm) 其中 n 是替换数组中的单词数,并且m请求中的字数。

由于所有模式似乎都在寻找单词(\b 前后),并且不使用任何其他正则表达式语法(仅文字字符),您可以通过将请求拆分为更好的性能单词并在关联数组中查找它们,根本不需要使用正则表达式。

因此将您的 pattern/replacement 数据定义为关联数组,如下所示:

$dict = array(
    "мувосокори" => "мувосокорӣ",
    "мунаггас" => "мунағғас",
    "мангит" => "манғит",
    "мангития" => "манғития",
    "мунфачир" => "мунфаҷир",
    "мунфачира" => "мунфаҷира",
    "манфиатпарасти" => "манфиатпарастӣ",
    "манфиатчу" => "манфиатҷӯ",
    "манфиатчуи" => "манфиатҷӯӣ",
    "манфиатхох" => "манфиатхоҳ",
    "манфи" => "манфӣ",
    ...........................
    "мусби" => "мусбӣ"
);

然后用preg_replace_callback找到request中的每一个词,在上面的字典中查找:

$response = preg_replace_callback("/\pL+/u", function ($m) use ($dict) {
    return isset($dict[$m[0]]) ? $dict[$m[0]] : $m[0];
}, $request);

时间复杂度与请求中的字数成线性关系。

处理upper/lower个案

如果您还需要匹配单词大小写的任何变化,那么将任何此类变化存储在字典中就太多了。相反,您可以将字典全部保留为小写字母,然后使用下面的代码。当与字典匹配时,它会检查原始单词的大小写,并将其应用于替换单词:

$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

上查看 运行

正在转换现有数组

您可以使用一小段代码将当前的 $patterns$replacements 数组转换为新的数据结构,以便避免你必须这样做 "manually":

foreach ($patterns as $i => $pattern) {
    $dict[explode("\b", $pattern)[1]] = $replacements[$i];
}

当然,您不应该在您的代码中包含此转换,而只是 运行 它一次以生成新的数组结构,然后将该数组文字放入您的代码中。