删除具有所有格量词正则表达式的 C 风格注释

Delete c-style comments with possessive quantifier regex

我想了解所有格量词的工作原理。是否可以使用此功能从字符串中删除例如注释? 这是我尝试过的:

use feature qw(say);
use strict;
use warnings;

my $str = ' abc /* hi */ def /* h*i */';

say $str =~ s{/\*[^*]++\*/}{}sgr;
say $str =~ s{/\*.*+\*/}{}sgr;
say $str =~ s{/\*.*?\*/}{}sgr;

输出:

 abc  def /* h*i */
 abc /* hi */ def /* h*i */
 abc  def 

为什么所有格量词在这里不起作用?

所有格量词做得很好。

输入字符串:

my $str = ' abc /* hi */ def /* h*i */';

正则表达式 1:

/\*[^*]++\*/

/\* - 匹配第一个 /* 和最后一个 /*.

[^*]++ - 不是 * 一次或多次。 ++ 不允许回溯。所以这首先匹配 /* hi 并且下面的模式 \*/ 匹配存在于 hi 旁边的 */ 。同时,正则表达式引擎尝试在下一个 /* 中做同样的工作。也就是说,/\* 匹配 h*i 之前存在的 /*。想出下一个模式 [^*]++ 匹配到 /* h,然后一旦找到 * 就停止匹配。然后它不会走得更远,即 (forward or backwards) 因为所有格 ++ 量词不允许回溯。所以第二部分失败了。

我们终于找到了 /* hi */ 的匹配项。用空字符串替换这些字符将为您提供 abc def /* h*i */

的输出

正则表达式 2:

/\*.*+\*/

/\* - 匹配 hih*i

之前存在的 /*

.* - 贪婪地匹配所有字符直到最后一个。

.*+ - 由于 * 之后的 + 不允许回溯,正则表达式标记位于行尾并且无法匹配 */ 因为行尾后不存在 */ 。所以这个正则表达式失败了,它甚至不会匹配输入字符串中的单个字符。

最后s{/\*.*+\*/}{}sgr returns输入字符串作为输出。(注意上面模式中的花括号是正则表达式分隔符

解法:

要删除评论部分,我建议您使用下面的正则表达式,

/\*.*?\*/

DEMO

/\*(?:(?!/\*|\*/).)*\*/

DEMO

用所有格量词来做到这一点的一种方法是使用这种模式,允许评论中的任何 *

$str =~ s{/\*(?:[^*]+|\*+(?!/))*+\*/}{}g

这个想法是在交替中分离两种可能的情况:

  1. 所有不是 *
  2. 的字符
  3. 一个或多个 * 后面没有 /

最后,只需要将包含交替的非捕获组的量词设为所有格即可。

但是交替和前瞻是有代价的。因此,另一种可能的方法是使用这种带有惰性量词的原子组的模式,而不是交替使用:

/\*(?>[^*]*\*+)+?/

请注意,它使用 + 量词来确保最终 * 的存在。 所以惰性量词对性能的影响非常有限(仅当满足一组*时)。

即使第二种方式使用惰性量词,它也可能是一种更快的方式并且需要几个步骤来匹配整个评论。

注意: 在大多数语言中,未用 */ 结束的 C 注释一直运行到文件末尾。让我们看看处理这种情况的两种模式的版本:

第一个模式:online demo(*)

/\*(?:[^*]+|\*+(?!/))*+(?:\*/|\z)

第二个模式:online demo(*)

/\*(?>[^*]*(?:\*+|\z(*ACCEPT)))+?/

(*)点击调试项查看每种方式所需的步骤数。