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');
我正在尝试 运行 一些输入数据的简单替换,可以描述如下:
- 取一个正则表达式
- 获取输入数据流
- 在每场比赛中,通过回调替换比赛
不幸的是,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');