PHP正则表达式用回调替换多个模式

时间:2019-05-23 13:19:48

标签: php regex pcre preg-replace-callback

我正在尝试对某些输入数据进行简单替换,如下所述:

  • 使用正则表达式
  • 获取输入数据流
  • 在每个匹配项上,通过回调替换匹配项

不幸的是,preg_replace_callback()无法正常工作。它给了我整行的所有比赛,而不是单个比赛。因此,更换后我需要将线重新放在一起,但是我没有足够的信息来做。例子:

<?php
echo replace("/^\d+,(.*),(.*),.*$/", "12,LOWERME,ANDME,ButNotMe")."\n";
echo replace("/^\d+-\d+-(.*) .* (.*)$/", "13-007-THISLOWER ThisNot THISAGAIN")."\n";


function replace($pattern, $data) {
    return preg_replace_callback(
        $pattern, 
        function($match) {
            return strtolower($match[0]);
        }, $data
    );
}

https://www.tehplayground.com/hE1ZBuJNtFiHbdHO

给我12,lowerme,andme,butnotme,但我想要12,lowerme,andme,ButNotMe

我知道使用$ match [0]是错误的。这只是为了说明。在闭包内部,我需要运行类似

foreach ($match as $m) { /* do something */ }

但是正如我所说,我不知道有关匹配项在输入字符串中的位置的信息,这使得无法再次将字符串重新组合在一起。

我已经遍历了PHP文档以及几次搜索,找不到解决方案。


说明:

我知道$ match [1],$ match [2] ...等包含匹配项。但是只有一个字符串,而不是位置。想象一下,在我的示例中,最后一个字符串也是ANDME而不是ButNotMe-根据正则表达式,应进行匹配,并且应对其应用回调。这就是为什么我首先使用正则表达式而不是字符串替换的原因。

此外,我以这种方式使用捕获组的原因是我需要替换过程是可配置的。因此,我无法对“替换#1和#2但不能替换#3”之类的东西进行硬编码。在不同的输入文件上,位置可能不同,或者可能需要替换的位置更多,并且只应更改使用的正则表达式。

因此,如果我的输入是"15,LOWER,ME,NotThis,AND,ME,AGAIN",我希望能够只更改正则表达式,而不是代码,并获得所需的结果。基本上,$ pattern和$ data都是可变的。

2 个答案:

答案 0 :(得分:1)

这使用preg_match()PREG_OFFSET_CAPTURE返回捕获组和原始字符串中找到它的位置的偏移量。然后,它对每个捕获组使用substr_replace()来仅替换要更改的字符串部分-这样就避免了替换不希望更改的相似文本的任何机会...

function lowerParts (string $input, string $regex ) {
    preg_match($regex, $input, $matches, PREG_OFFSET_CAPTURE);
    array_shift($matches);
    foreach ( $matches as $match )  {
        $input = substr_replace($input, strtolower($match[0]),
            $match[1], strlen($match[0]));
    }
    return $input;
}
echo lowerParts ("12,LOWERME,ANDME,ButNotMe", "/^\d+,(.*),(.*),.*$/");

给予...

12,lowerme,andme,ButNotMe

而且

echo lowerParts ("12,LOWERME,ANDME,LOWERME", "/^\d+,(.*),(.*),.*$/");

它给出了

12,lowerme,andme,LOWERME

编辑:

如果替换数据的长度不同,则需要将字符串切成小段并替换每个字符串。复杂的是,每次长度变化都会改变偏移量的相对位置,因此必须跟踪该偏移量是多少。此版本还具有一个参数,该参数是您要应用于字符串的过程(此示例仅传递"strtolower")...

function processParts (string $input, string $regex, callable $process ) {
    preg_match($regex, $input, $matches, PREG_OFFSET_CAPTURE);
    array_shift($matches);
    $offset = 0;
    foreach ( $matches as $match )  {
        $replacement = $process($match[0]);
        $input = substr($input, 0, $match[1]+$offset)
                 .$replacement.
                 substr($input, $match[1]+$offset+strlen($match[0]));
        $offset += strlen($replacement) - strlen($match[0]);
    }
    return $input;
}
echo processParts ("12,LOWERME,ANDME,LOWERME", "/^\d+,.*,(.*),(.*)$/", "strtolower");

答案 1 :(得分:1)

这将起作用:

function replaceGroups(string $pattern, string $string, callable $callback)
{
    preg_match($pattern, $string, $matches, PREG_OFFSET_CAPTURE);
    array_shift($matches);

    foreach (array_reverse($matches) as $match) {
        $string = substr_replace($string, $callback($match[0]), $match[1], mb_strlen($match[0]));
    }

    return $string;
}

echo replaceGroups("/^\d+-\d+-(.*) .* (.*)$/", "13-007-THISLOWER ThisNot THISAGAIN", 'strtolower');