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

RMa*_*tin 11 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}
Run Code Online (Sandbox Code Playgroud)

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

$str = preg_replace('~(?<=A)(\{n\})*(?=A)~', 'C', $str);
Run Code Online (Sandbox Code Playgroud)

问题是它将两个A之间的所有实例替换为一个C.我怎样才能修复我的正则表达式或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}
Run Code Online (Sandbox Code Playgroud)

但目前它输出了这个.

{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}
Run Code Online (Sandbox Code Playgroud)

hwn*_*wnd 8

您可以通过锚定来解决问题\G.

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

\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);
Run Code Online (Sandbox Code Playgroud)

  • [+](http://en.wikipedia.org/wiki/Plus_and_minus_signs)1对于`\ K`的使用很棒. (3认同)

Ja͢*_*͢ck 7

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

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

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

  • 老实说,如果性能问题,可能是一个更快的解决方案.(1) (2认同)