在PHP中将括号内的文本替换为这样命名的变量


Replace text within brackets with thus-named variable in PHP

我想将方括号([])中的所有字符串替换为从命名为该字符串的数组中随机选择的项。

这与这个问题非常相似,但有一点不同,因为我想用名为它的数组中的字符串替换不同括号的内容。

一个例子应该会让这一点更加清晰

假设我有字符串

"This is a very [adjective] [noun], and this is a [adjective] [noun]."

变量:

$adjective = array("big","small","good","bad");
$noun      = array("house","dog","car");

我们希望它通过随机选择返回"This is a very big house, and this is a good dog."或其他什么。也就是说,我想编写一个PHP函数,用从名为$string的数组中随机选择的项来替换每个[string]。目前,随机选择是否会重复选择并不重要,但它必须为每个[]项目做出新的选择。

我希望我已经解释清楚了。如果你能实现我想要实现的目标,并且能想出更好的方法,我将不胜感激。

算法

  1. 匹配此正则表达式:('[.*?'])
  2. 对于每个匹配组,从相关数组中选择一个项目
  3. 按顺序替换字符串

实施

$string    = "This is a very [adjective] [noun], and this is a [adjective] [noun].";
$adjective = array("big","small","good","bad");
$noun      = array("house","dog","car");
// find matches against the regex and replaces them the callback function.
$result    = preg_replace_callback(
                 // Matches parts to be replaced: '[adjective]', '[noun]'
                 '/('[.*?'])/',
                 // Callback function. Use 'use()' or define arrays as 'global'
                 function($matches) use ($adjective, $noun) {
                     // Remove square brackets from the match
                     // then use it as variable name
                     $array = ${trim($matches[1],"[]")};
                     // Pick an item from the related array whichever.
                     return $array[array_rand($array)];
                 },
                 // Input string to search in.
                 $string
             );
print $result;

解释

preg_replace_callback函数使用提供的回调函数执行正则表达式搜索和替换。

  • 第一个参数是要匹配的正则表达式(括在斜杠之间):/('[.*?'])/

  • 第二个参数是为每个匹配调用的回调函数。将当前匹配项作为参数。

    • 我们必须在这里使用use()从函数内部访问数组,或者将数组定义为global:global $adjective = ...。也就是说,我们必须执行以下操作之一:

      a)将阵列定义为global:

      。。。global$形容词=数组("大"、"小"、"好"、"坏");global$norn=数组("house"、"dog"、"car");。。。函数($matches){。。。

      b)使用use:

      。。。$形容词=数组("大"、"小"、"好"、"坏");$norn=数组("房子"、"狗"、"车");。。。函数($matches)用法($形容词,$名词){。。。
    • 回调函数的第一行:

      • trim:使用trim函数从匹配中删除方括号([])。

      • ${}:创建一个变量,用作具有匹配名称的数组名称。例如,如果$match[noun],则trim($matches[1],"[]")返回noun(不带括号),并且${noun}变为数组名称:$noun。有关该主题的更多信息,请参阅变量

    • 第二行随机选择可用于$array的索引号,然后返回该位置的元素。

  • 第三个参数是输入字符串。

下面的代码将完成这项工作:

$string = "This is a very [adjective] [noun], and this is a [adjective] [noun]."
function replace_word ( $matches )
{
    $replaces = array(
        '[adjective]'  =>  array("big", "small", "good", "bad"),
        '[noun]'  =>  array("house", "dog", "car")
    );
    return $replaces[$matches[0]][array_rand($replaces[ $matches[0] ])];
}
echo preg_replace_callback("('[.*?'])", "replace_word", $string);

首先,我们对单词的[something]部分进行正则表达式匹配,并用preg_replace_callback()调用其上的replace_word()回调函数。此函数内部定义了一个内部$replaces二维深数组,每行都以[word type] => array('rep1', 'rep2', ...)格式定义。

return $replaces[$matches[0]][array_rand($replaces[ $matches[0] ])];是一条棘手且有点模糊的线路。如果我把它压缩一点,它对你来说会更节省:

$random = array_rand( $replaces[ $matches[0] ] );

$matches[0]是单词类型,这是我们正在搜索的$replaces数组中的关键字。这是通过原始字符串中的正则表达式找到的。array_rand()基本上选择数组中的一个元素,并返回其数值索引。所以$random现在是一个介于0和包含替换的数组的(number of elements - 1)之间的整数。

return $replaces[ $matches[0] ][$random];

这将返回替换数组中的第33个CCD_元素。在代码片段中,这两行被放在一起成为一行。

只显示一个元素一次

如果你想要析取元素(没有两个形容词或名词重复两次),那么你需要做另一个技巧。我们将把$replaces数组设置为不在replace_word()函数内部定义,而是在它外部定义

$GLOBALS['replaces'] = array(
    '[adjective]'  =>  array("big", "small", "good", "bad"),
    '[noun]'  =>  array("house", "dog", "car")
);

在函数内部,我们将通过调用$replaces = &$GLOBALS['replaces'];,将本地$replaces变量设置为对新设置的数组的引用。(&操作符将其设置为引用,因此我们对$replaces所做的一切(例如,移除和添加元素)也会修改原始数组。如果没有它,它将只是一个副本。)

在到达return行之前,我们在当前要返回的密钥上调用unset()

unset($replaces[$matches[0]][array_rand($replaces[ $matches[0] ])]);

现在组合在一起的函数如下:

function replace_word ( $matches )
{
    $replaces = &$GLOBALS['replaces'];
    unset($replaces[$matches[0]][array_rand($replaces[ $matches[0] ])]);
    return $replaces[$matches[0]][array_rand($replaces[ $matches[0] ])];
}

由于$replaces是对全局的引用,因此unset()也会更新原始数组。replace_word()的下一次调用将不会再次找到相同的替换。

小心数组的大小

如果字符串包含的替换变量超过了现有替换值的数量,则会抛出未定义索引E_NOTICE。以下字符串不起作用:

$string = "This is a very [adjective] [noun], and this is a [adjective] [noun]. This is also an [adjective] [noun] with an [adjective] [noun].";

其中一个输出看起来如下,表明我们已经用完了可能的替代品:

这是一栋很大的房子,这是一座很大的房子。这也是一个带有的小。

另一种很好(更容易)的方法(不是我的解决方案)

https://stackoverflow.com/a/15773754/2183699

使用foreach检查要替换的变量,并用替换它们

str_replace();

您可以使用preg_match和str_replace函数来实现此目标。

  • 首先使用preg_match函数查找匹配项,然后创建search&从结果中替换数组
  • 通过将前面的数组作为参数传递来调用str_replace函数

这是我对mmdemirbas上述答案的小更新。它允许您在函数之外设置变量(如前所述,使用全局变量)。

$result    = preg_replace_callback(
                 // Matches parts to be replaced: '[adjective]', '[noun]'
                 '/('[.*?'])/',
                 // Callback function. Use 'use()' or define arrays as 'global'
                 function($matches) use ($adjective, $noun) {
                     // Remove square brackets from the match
                     // then use it as variable name
                    $arrayname = trim($matches[1],"[]");
                    $array = $GLOBALS[$arrayname];
                     // Pick an item from the related array whichever.
                     return $array[array_rand($array)];
                 },
                 // Input string to search in.
                 $string
             );
print $result;