PHP preg_match_all搜索和替换

8

我已经尝试了无数种不同的正则表达式,但是这一个我就是理解不了(承认很多正则表达式超出了我的掌握范围)。

在我的文本中,我有这样的变量:

{{$one}}
{{$three.four.five}}
{{$six.seven}}

我有一个包含所有替换内容的数组('one'的索引是'one'等等),但有些可能缺失。

如果存在于数组中,则希望替换文本,否则保留原样。

我该使用什么正则表达式来preg_match_all $text变量中的代码段中的变量,在适当情况下从$replace进行替换并在浏览器中输出?

<?php
    $replaces = array('testa.testb.testc' => '1', 'testc.testa' => '2', 'testf' => '3');
    $text = '{{$testa.testb.testc}}<br>{{$testc.testa}}<br>{{$testf}}<br>{{$aaaaa}}<br>';

    preg_match_all('/\{\{\$(\w+)\}\}/e', $text, $matches);

    foreach($matches as $match)
    {
        $key = str_replace('{{$', '', $match);
        $key = str_replace('}}', '', $key);

        if(isset($replaces[$key]))
            $text = str_replace($match, $replaces[$key], $text);
    }

    // I want to end up echo'ing:
    //   1<br>2<br>3<br>{{$aaaaa}}<br>

    echo $text;
?>

http://codepad.viper-7.com/4INvEE

This:

'/\{\{\$(\w+)\}\}/e'

就像代码片段中的一样,这是我能做到的最接近的。

它也必须与变量名中的“does”一起工作。

非常感谢您的所有帮助!


你看看$matches里面有什么了吗?如果你看一下我在你的问题中添加的链接,你会注意到你的loop没有像你想象的那样工作。另外,你也没有允许在捕获中加入. - Jared Farrish
这个链接(http://codepad.viper-7.com/xw9j1u)可以帮助你完成大部分工作;我对正则表达式也不是很擅长,所以如何将`\w+`与`.`结合起来,并允许它捕获一个或多个(而不仅仅是三个),我并不完全确定该怎么做。 - Jared Farrish
4个回答

8

这是一个非常适合使用preg_replace_callback()的案例,但首先让我们改进你的正则表达式:

  1. Get rid of the e modifier, it's deprecated and you don't need it since we're going to use preg_replace_callback()

    /\{\{\$(\w+)\}\}/
    
  2. We don't need to escape {{}} in this case, PCRE is smart enough to tell that they are not meant as quantifiers

    /{{\$(\w+)}}/
    
  3. Since you got dots in your input, we need to change \w otherwise it will never match. [^}] is perfect since it means match anything except }

    /{{\$([^}]+)}}/
    
  4. I tend to use different delimiters, this is not required:

    #{{\$([^}]+)}}#
    

让我们来认真对待技术问题,use标识符在这里会非常有帮助:

$replaces = array('testa.testb.testc' => '1', 'testc.testa' => '2', 'testf' => '3');
$text = '{{$testa.testb.testc}}<br>{{$testc.testa}}<br>{{$testf}}<br>{{$aaaaa}}<br>';

$output = preg_replace_callback('#{{\$([^}]+)}}#', function($m) use ($replaces){
    if(isset($replaces[$m[1]])){ // If it exists in our array
        return $replaces[$m[1]]; // Then replace it from our array
    }else{
        return $m[0]; // Otherwise return the whole match (basically we won't change it)
    }
}, $text);

echo $output;

在线正则表达式演示 在线PHP演示


2
太棒了!从头到尾都有详细的解释。谢谢。 - Alex Howe
1
@HamZa 我不知道我怎么回到这里,然后注意到你甚至可以这样做:return isset($replaces[ $m[1] ]) ? $replaces[ $m[1] ] : $m[0]; 哈哈 :P - Bobot
1
@Bobot,你让我回到了很久以前。确实,那也是可能的! - HamZa

2
你只使用了 $matches,而没有用 $matches[0],这就是你发布的代码无法正常工作的原因。
并且你的正则表达式没有将 "." 和 "\w" 计数在内,所以让我们尝试使用 "[\w.]+"。
(我使用了 $matches[1],它直接包含我们需要的键,因此我们不需要再使用两次 str_replace)
$replaces = array('testa.testb.testc' => '1', 'testc.testa' => '2', 'testf' => '3');
$text = '{{$testa.testb.testc}}<br>{{$testc.testa}}<br>{{$testf}}<br>{{$aaaaa}}<br>';

preg_match_all('/\{\{\$([\w.]+)\}\}/', $text, $matches);

var_dump($matches[1]);

foreach($matches[1] as $match)
{
    if(isset($replaces[$match]))
    {
        $text = str_replace('{{$'.$match.'}}', $replaces[$match], $text);
    }
}

echo $text;

这个功能正常工作并返回如下结果:
1
2
3
{{$aaaaa}}

刚刚看到HamZa的答案,我就放弃了我的,因为我发现他的更简单 :) - Bobot
1
不错的方法 +1,我冒昧去掉了 e 修饰符并取消了点号的转义。基本上,在字符类中不需要转义点号 .,它将失去其正则表达式的含义。因此,[.] 只会匹配一个点号。 - HamZa
1
非常感谢,我对于转义或不转义等问题感到很困惑.. ^^ - Bobot

2

你不需要使用正则表达式,因为你只需要处理固定字符串:

$replaces = array('testa.testb.testc' => 1, 'testc.testa' => 2, 'testf' => 3);
$keys = array_map(function ($item) {
    return '{{$' . $item . '}}'; }, array_keys($replaces));
$trans = array_combine($keys, $replaces);

$result = strtr($text, $trans);

请注意,如果您像这样编写替换数组,则无需使用array_map
$trans = array('{{$testa.testb.testc}}' => 1, '{{$testc.testa}}' => 2, '{{$testf}}' => 3);

你的回答让我想起了这个问题:https://dev59.com/yGUo5IYBdhLWcg3wxB3T。基本上,简单明了值得点赞。 - HamZa
@HamZa:谢谢!<°))))))))))> - Casimir et Hippolyte

0

你也可以使用 T-Regx 库,它具有自动分隔符。

使用 replace()->by()->mapIfExists()

$output = pattern('{{\$([^}]+)}}')
    ->replace('{{$testa.testb.testc}}<br>{{$testc.testa}}<br>{{$testf}}<br>{{$aaaaa}}<br>')
    ->all()
    ->by()
    ->group(1)
    ->mapIfExists([
       'testa.testb.testc' => '1', 
       'testc.testa' => '2',
       'testf' => '3'
    ]);
});

echo $output;

这段代码与@HamZa的答案完全相同,但使用了T-Regx :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接