<p>我建议用双重否定来解决这个问题:</p>
<pre><code>(?= # inside lookahead for overlapping results
(?<![^a]) # match at beginning of str or after a
(b*ab*) # one a between any number of bs
(?![^a]) # at end of str or before next a
)
</code></pre>
<p>参见<a href="https://regex101.com/r/FO8FOr/1" rel="nofollow noreferrer">regex demo</a></p>
<p>注意我用lookarounds替换了<em>分组</em>结构:<code>(?:a|^)</code>替换为<code>(?<![^a])</code>,<code>(?:a|$)</code>替换为<code>(?![^a])</code>。后者并不重要,但第一个在这里非常重要。你知道吗</p>
<p>外部lookahead模式开头的<code>(?:a|^)</code>与字符串的<code>a</code>或开头匹配,无论哪个先到。如果<code>a</code>在开始处,那么它是匹配的,当输入是<code>abbabb</code>时,您会得到<code>bbabb</code>,因为它匹配捕获组模式,并且后面有一个字符串结束位置。下一次迭代在第一个<code>a</code>之后开始,并且找不到任何匹配项,因为字符串中只剩下的<code>a</code>在<code>b</code>之后没有<code>a</code></p>
<p>注意<a href="https://stackoverflow.com/questions/18017661/why-does-the-order-of-alternatives-matter-in-regex">order of alternative matters</a>。如果更改为<code>(?:^|a)</code>,则匹配从字符串的开头开始,<code>b*</code>匹配空字符串,<code>ab*</code>获取<code>abbabb</code>中的第一个<code>abb</code>,并且由于紧随其后的是<code>a</code>,因此将<code>abb</code>作为匹配。无法匹配第一个<code>a</code>之后的任何内容。你知道吗</p>