替换两个字符之间的每个实例

时间:2015-02-13 03:58:38

标签: php regex preg-replace

我在下面的数据中{n}表示占位符。

{n}{n}A{n}{n}A{n}
{n}A{n}{n}{n}{n}A
{n}{n}A{n}A{n}{n}
{n}{n}{n}A{n}A{n}B
{n}A{n}{n}B{n}{n}
A{n}B{n}{n}{n}{n}

我想用两个A字符替换占位符的每个实例,例如字母C。我为此编写了以下正则表达式,并且我使用了preg_replace函数。

$str = preg_replace('~(?<=A)(\{n\})*(?=A)~', 'C', $str);

问题在于它用两个C替换了两个A之间的所有实例。如何修复我的正则表达式或preg_replace调用以用C替换占位符的每个单独实例?

这应该是我的输出。

{n}{n}ACCA{n}
{n}ACCCCA
{n}{n}ACA{n}{n}
{n}{n}{n}ACA{n}B
{n}A{n}{n}B{n}{n}
A{n}B{n}{n}{n}{n}

但目前它输出了这个。

{n}{n}ACA{n}
{n}ACA
{n}{n}ACA{n}{n}
{n}{n}{n}ACA{n}B
{n}A{n}{n}B{n}{n}
A{n}B{n}{n}{n}{n}

3 个答案:

答案 0 :(得分:8)

您可以使用\G进行锚定来解决问题。

$str = preg_replace('~(?:\G(?!\A)|({n})*A(?=(?1)++A))\K{n}~', 'C', $str);

\G功能是一个可以在两个位置之一匹配的锚点;字符串位置的开头或最后一个匹配结束时的位置。 \K转义序列重置报告的匹配的起始点,并且不再包括任何以前消耗的字符。

为了减少回溯量,您可以使用更复杂的表达式:

$str = preg_replace('~\G(?!\A)(?:{n}
                      |A(?:[^A]*A)+?((?=(?:{n})++A)\K{n}
                      |(*COMMIT)(*F)))
                      |[^A]*A(?:[^A]*A)*?(?1)~x', 'C', $str);

答案 1 :(得分:7)

更详细但更容易理解的解决方案是使用初始表达式将文本分成组;然后在每个组中应用单个转换:

$text = preg_replace_callback('~(?<=A)(?:\{n\})*(?=A)~', function($match) {
    // simple replacement inside
    return str_replace('{n}', 'C', $match[0]);
}, $text);

我使用(?:...)对表达式进行了一些小调整以消除内存捕获,这是不必要的。

答案 2 :(得分:4)

(?<=A){n}(?=(?:{n})*A)|\G(?!^){n}

你可以试试这个。替换为C。在这里,您必须使用\G来确定上一场比赛结束时的位置或第一场比赛的字符串开头。

这样你就可以在第一场比赛后进行比赛。见演示。

https://regex101.com/r/wU4xK1/7

首先,您匹配{n}后面有A的{​​{1}}和A后面的{n}。捕获后,您使用\G重置为上一个匹配结束,然后继续替换找到的{n}

$re = "/(?<=A){n}(?=(?:{n})*A)|\\G(?!^){n}/";
$str = "{n}{n}A{n}{n}A{n}\n{n}A{n}{n}{n}{n}A\n{n}{n}A{n}A{n}{n}\n{n}{n}{n}A{n}A{n}B\n{n}A{n}{n}B{n}{n}\nA{n}B{n}{n}{n}{n}";
$subst = "C";

$result = preg_replace($re, $subst, $str);