PHP Regex preg_replace 函数仅查找和替换 3 个匹配项中的第一个和最后一个,而不是中间一个

Posted

技术标签:

【中文标题】PHP Regex preg_replace 函数仅查找和替换 3 个匹配项中的第一个和最后一个,而不是中间一个【英文标题】:PHP Regex preg_replace function finds and replaces only the first and last of 3 matches, not the middle one 【发布时间】:2022-01-23 23:58:06 【问题描述】:

我有以下 javascript-excerpt-as-text

for (let orange of oranges) 

  for (let apple of apples) 

    for (let banana of bananas) 

      obfuscatedArray[i] = obfuscatedArray[i].split('').reverse().join('');
      obfuscatedArray[i] = window.atob(obfuscatedArray[i]);

    

  


我想从中删除底部多余的换行符:

for (let orange of oranges) 

  for (let apple of apples) 

    for (let banana of bananas) 

      obfuscatedArray[i] = obfuscatedArray[i].split('').reverse().join('');
      obfuscatedArray[i] = window.atob(obfuscatedArray[i]);
    
  

我已经写了这个正则表达式:

/(;|)(\n(\h*))+/

在以下 php 中:

$myString = preg_replace('/(;|)(\n(\h*))+/', "\$1\n\$3", $myString);

但是,由于我无法确定的原因,第一个右大括号和第二个大括号之间的换行符没有被删除。

我已经在 Regex101 中测试了正则表达式(即在 PHP 的 preg_replace() 函数之外),它仍然只找到两个匹配项而不是三个。

我真的不明白我的正则表达式哪里出错了?

【问题讨论】:

您的模式匹配新行之前和新行之后的大括号,您不能匹配两次相同的字符。 (第二个大括号)。 @CasimiretHippolyte - 啊哈。谢谢你。我害怕它可能是那样的。你知道当一个匹配中包含的角色也需要包含在以后的匹配中时,通常的方法是什么?我需要在while 循环中运行preg_replace 吗?或者是否有计算强度较低的替代方案? 不,您不需要 while 循环,将最后一个 放入不消耗字符的前瞻断言 (?=)后跟 ) ,这只是一个测试。 你甚至可以在不使用捕获组的情况下做到这一点:regex101.com/r/Z7vovK/1 @CasimiretHippolyte - 啊,太棒了!谢谢你。正面和负面的 lookaheadslookbehinds - 我几乎没有使用它们。如果您想将上面的评论复制粘贴到下面的答案中,我会接受。 【参考方案1】:

您的模式正在匹配最后一行与 并且无法再次匹配以参与下一次匹配尝试。

如果您想替换其间的所有“空”行,您可以更改模式以断言换行符后跟右侧的水平空白字符,然后是 以不使用它。

(;|)(\n(\h*))+(?=\n\h*)

在替换使用组1$1

Regex demo


也可以使用\K 写入模式,省略第一个捕获组,然后省略其他多余的捕获组,字符类[;] 而不是交替,并使用\R 来匹配任何Unicode 换行序列只有一个换行符:

[;]\K(?:\R\h*)*(?=\R\h*)

在替换中使用空字符串。

Regex demo


由于您想匹配其间的所有“空”行,您可以将 (?:\R\h*)* 替换为 \s*,将模式缩短为:

[;]\K\s*(?=\R\h*)

Regex demo

模式匹配:

[;] 匹配 ; \K忘记到目前为止匹配的内容(清除当前匹配缓冲区) \s* 匹配可选的空白字符 (?=\R\h*) 正向前瞻,从当前位置断言换行符,可选水平空白字符和

【讨论】:

【参考方案2】:

您在一个或多个换行符之后使用(即匹配并将匹配的文本添加到整个匹配内存缓冲区并推进正则表达式索引);。一旦一个子字符串被消费,下一个匹配就不能消费相同的文本。

您可以使用环视来覆盖它:

preg_replace('~([;])\h*\R(?=\h*(?:\R\h*)+)~', '$1', $text)
preg_replace('~(?<=[;])\h*\R(?=\h*(?:\R\h*)+)~', '', $text)
preg_replace('~[;]\K\h*\R(?=\h*(?:\R\h*)+)~', '', $text)

请参阅regex demo(或this regex demo)。

请注意,在最后两个示例中,不需要使用 $1 反向引用,因为模式中没有捕获组,它被替换为不消耗的后向 ((?&lt;=[;])) 或 \K用于清除当前匹配内存缓冲区。

详情

([;]) - 捕获组 #1:; 字符 (?&lt;=[;]) - 正向的后视,要求 ; 立即出现在当前位置的左侧 [;]\K - ; 然后 \K 运算符“丢失”匹配的文本(从匹配内存缓冲区中删除 ;\h* - 零个或多个水平空格 \R - 换行序列 (?=\h*(?:\R\h*)+) - 与紧随其后的位置匹配的正向前瞻 \h* - 零个或多个水平空格 (?:\R\h*)+ - 出现一次或多次换行序列和零个或多个水平空格 - 字符。

【讨论】:

以上是关于PHP Regex preg_replace 函数仅查找和替换 3 个匹配项中的第一个和最后一个,而不是中间一个的主要内容,如果未能解决你的问题,请参考以下文章

PHP 删除/替换 - href / anchor / link - 在html / string中 - preg_replace - php - regex - 正则表达式

JavaScript 等价于 PHP 的 preg_replace

PHP preg_replace - 正则表达式

PHP regex-从字符串中删除特殊字符

php正则替换函数-----preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1

PHP Regex 仅允许 a-z A-Z 1-9 并用下划线替换空格