我最近问了一个类似的问题,但没有得到一个明确的答案,因为我太具体了。这个更广泛。
有谁知道如何用正则表达式模式替换(x)事件?
示例:假设我想替换字符串中第5次出现的正则表达式。我该怎么做?
这是模式:
preg_replace('/{(.*?)\|\:(.*?)}/', 'replacement', $this->source);
@anubhava请求的示例代码(最后一个函数不起作用):
$sample = 'blah asada asdas {load|:title} steve jobs {load|:css} windows apple ';
$syntax = new syntax();
$syntax->parse($sample);
class syntax {
protected $source;
protected $i;
protected $r;
// parse source
public function parse($source) {
// set source to protected class var
$this->source = $source;
// match all occurrences for regex and run loop
$output = array();
preg_match_all('/\{(.*?)\|\:(.*?)\}/', $this->source, $output);
// run loop
$i = 0;
foreach($output[0] as $key):
// perform run function for each occurrence, send first match before |: and second match after |:
$this->run($output[1][$i], $output[2][$i], $i);
$i++;
endforeach;
echo $this->source;
}
// run function
public function run($m, $p, $i) {
// if method is load perform actions and run inject
switch($m):
case 'load':
$this->inject($i, 'content');
break;
endswitch;
}
// this function should inject the modified data, but I'm still working on this.
private function inject($i, $r) {
$output = preg_replace('/\{(.*?)\|\:(.*?)\}/', $r, $this->source);
}
}
答案 0 :(得分:8)
你误解了正则表达式:它们是无状态的,没有记忆,没有能力计数,没有,所以你不能知道匹配是字符串中的第x个匹配 - 正则表达式引擎没有我有一个线索。出于同样的原因你不能用正则表达式来做这种事情,因为不可能写一个正则表达式来查看字符串是否有平衡括号:问题需要一个内存,根据定义,正则表达式没有。< / p>
但是,正则表达式引擎可以告诉您所有匹配项,因此最好使用preg_match()
来获取匹配项列表,然后自己使用该信息修改字符串。
更新:这更接近您的想法吗?
<?php
class Parser {
private $i;
public function parse($source) {
$this->i = 0;
return preg_replace_callback('/\{(.*?)\|\:(.*?)\}/', array($this, 'on_match'), $source);
}
private function on_match($m) {
$this->i++;
// Do what you processing you need on the match.
print_r(array('m' => $m, 'i' => $this->i));
// Return what you want the replacement to be.
return $m[0] . '=>' . $this->i;
}
}
$sample = 'blah asada asdas {load|:title} steve jobs {load|:css} windows apple ';
$parse = new Parser();
$result = $parse->parse($sample);
echo "Result is: [$result]\n";
这给了......
Array
(
[m] => Array
(
[0] => {load|:title}
[1] => load
[2] => title
)
[i] => 1
)
Array
(
[m] => Array
(
[0] => {load|:css}
[1] => load
[2] => css
)
[i] => 2
)
Result is: [blah asada asdas {load|:title}=>1 steve jobs {load|:css}=>2 windows apple ]
答案 1 :(得分:4)
一个更简单,更清晰的解决方案,它也处理反向引用:
function preg_replace_nth($pattern, $replacement, $subject, $nth=1) {
return preg_replace_callback($pattern,
function($found) use (&$pattern, &$replacement, &$nth) {
$nth--;
if ($nth==0) return preg_replace($pattern, $replacement, reset($found) );
return reset($found);
}, $subject,$nth );
}
echo preg_replace_nth("/(\w+)\|/", '${1} is the 4th|', "|aa|b|cc|dd|e|ff|gg|kkk|", 4);
输出| aa | b | cc | dd是第4个| e | ff | gg | kkk |
答案 2 :(得分:1)
没有文字方法来匹配模式/pat/
的出现5。但您可以匹配/^(.*?(?:pat.*?){4,4})pat/
并替换为\1repl
。这将替换前4次出现,加上后面的任何内容,使用相同的内容,第五次使用repl。
如果/pat/
包含捕获组,则需要对前N-1个匹配使用非捕获等效项。替换模式应从\\2
开始引用捕获的组。
实现如下:
function replace_occurrence($pat_cap,$pat_noncap,$repl,$sample,$n)
{
$nmin = $n-1;
return preg_replace("/^(.*?(?:$pat_noncap.*?){".
"$nmin,$nmin".
"})$pat_cap/",$r="\\1$repl",$sample);
}
答案 3 :(得分:1)
这是替代方法:
$parts = preg_split('/\{((?:.*?)\|\:(?:.*?))\}/', $this->source, PREG_SPLIT_DELIM_CAPTURE);
$ parts将包含偶数偏移的原始字符串部分[0] [2] [4] [6] [8] [10] ......
匹配的分隔符将在[1] [3] [5] [7] [9]
例如,要查找第5次出现,您可以修改元素$n*2 - 1
,在这种情况下将是元素[9]:
$parts[5*2 - 1] = $replacement.
然后重新组装一切:
$output = implode($parts);
答案 4 :(得分:1)
正如已经说过的,正则表达式没有状态,你不能通过传递一个整数来确定替换的完全匹配...你可以将替换包装到一个找到所有匹配并仅替换的方法中以整数
给出的第n个匹配<?
function replace_nth_occurence ( &$haystack, $pattern, $replacement, $occurence) {
preg_match_all($pattern, $haystack, $matches, PREG_OFFSET_CAPTURE);
if(array_key_exists($occurence-1, $matches[0])) {
$haystack = substr($haystack, 0, $matches[0][$occurence-1][1]).
$replacement.
substr($haystack,
$matches[0][$occurence-1][1] +
strlen($matches[0][$occurence-1][0])
);
}
}
$haystack = "test0|:test1|test2|:test3|:test4|test5|test6";
printf("%s \n", $haystack);
replace_nth_occurence( $haystack, '/\|:/', "<=>", 2);
printf("%s \n", $haystack);
?>
答案 5 :(得分:0)
我的第一个想法是使用preg_replace进行回调并在回调中进行计数,正如其他用户所做的那样(非常好)。
或者,您可以使用preg_split使用PREG_SPLIT_DELIM_CAPTURE来保留分隔符,并在结果数组中进行实际替换。 PHP只捕获捕获parens之间的内容,因此您要么必须自己调整正则表达式,要么自己处理其他捕获。假设有1个捕获对,那么捕获的分隔符将始终位于奇数编号的索引中:1,3,5,7,9,....你需要索引9;并再次implode。
这意味着您需要进行一次捕获
$sample = "blah asada asdas {load|:title} steve jobs {load|:css} windows apple\n";
$sample .= $sample . $sample; # at least 5 occurrences
$parts = preg_split('/(\{.*?\|\:.*?\})/', $sample, -1, PREG_SPLIT_DELIM_CAPTURE);
$parts[9] = 'replacement';
$return = implode('', $parts);