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

PHP regex replace multiple patterns with 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 - 根据正则表达式,它应该 not 匹配并且回调应该 not应用于它。这就是为什么我首先使用正则表达式而不是字符串替换的原因。

此外,我以这种方式使用捕获组的原因是我需要替换过程是可配置的。所以我不能硬编码像 "replace #1 and #2 but not #3" 这样的东西。在不同的输入文件上,位置可能不同,或者可能需要更多的替换,只有使用的正则表达式应该改变。

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

这使用 preg_match()PREG_OFFSET_CAPTURE 到 return 捕获组和找到它的原始字符串中的偏移量。然后将 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");

这会起作用:

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