将每个实例替换为两个字符


Replace each instance between two characters

下面有以下数据,其中{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}

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

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

问题是它用一个C替换了两个A之间的所有实例。如何修复regex或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}

您可以通过锚定'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);

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

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

我对表达式做了一个小的调整,通过使用(?:...)来消除不必要的内存捕获。

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

你可以试试这个。替换为C。在这里,您必须使用'G在上一个匹配的末尾或第一个匹配的字符串的开头断言位置。

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

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

在这里,首先匹配后面有A{n}和后面可以有{n}A。捕获后,使用'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);