删除具有所有格量词正则表达式的 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:
/\*.*+\*/
/\*
- 匹配 hi
和 h*i
之前存在的 /*
.*
- 贪婪地匹配所有字符直到最后一个。
.*+
- 由于 *
之后的 +
不允许回溯,正则表达式标记位于行尾并且无法匹配 */
因为行尾后不存在 */
。所以这个正则表达式失败了,它甚至不会匹配输入字符串中的单个字符。
最后s{/\*.*+\*/}{}sgr
returns输入字符串作为输出。(注意上面模式中的花括号是正则表达式分隔符)
解法:
要删除评论部分,我建议您使用下面的正则表达式,
/\*.*?\*/
或
/\*(?:(?!/\*|\*/).)*\*/
用所有格量词来做到这一点的一种方法是使用这种模式,允许评论中的任何 *
:
$str =~ s{/\*(?:[^*]+|\*+(?!/))*+\*/}{}g
这个想法是在交替中分离两种可能的情况:
- 所有不是
*
的字符
- 一个或多个
*
后面没有 /
最后,只需要将包含交替的非捕获组的量词设为所有格即可。
但是交替和前瞻是有代价的。因此,另一种可能的方法是使用这种带有惰性量词的原子组的模式,而不是交替使用:
/\*(?>[^*]*\*+)+?/
请注意,它使用 +
量词来确保最终 *
的存在。
所以惰性量词对性能的影响非常有限(仅当满足一组*
时)。
即使第二种方式使用惰性量词,它也可能是一种更快的方式并且需要几个步骤来匹配整个评论。
注意: 在大多数语言中,未用 */
结束的 C 注释一直运行到文件末尾。让我们看看处理这种情况的两种模式的版本:
第一个模式:online demo(*)
/\*(?:[^*]+|\*+(?!/))*+(?:\*/|\z)
第二个模式:online demo(*)
/\*(?>[^*]*(?:\*+|\z(*ACCEPT)))+?/
(*)点击调试项查看每种方式所需的步骤数。
我想了解所有格量词的工作原理。是否可以使用此功能从字符串中删除例如注释? 这是我尝试过的:
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:
/\*.*+\*/
/\*
- 匹配 hi
和 h*i
/*
.*
- 贪婪地匹配所有字符直到最后一个。
.*+
- 由于 *
之后的 +
不允许回溯,正则表达式标记位于行尾并且无法匹配 */
因为行尾后不存在 */
。所以这个正则表达式失败了,它甚至不会匹配输入字符串中的单个字符。
最后s{/\*.*+\*/}{}sgr
returns输入字符串作为输出。(注意上面模式中的花括号是正则表达式分隔符)
解法:
要删除评论部分,我建议您使用下面的正则表达式,
/\*.*?\*/
或
/\*(?:(?!/\*|\*/).)*\*/
用所有格量词来做到这一点的一种方法是使用这种模式,允许评论中的任何 *
:
$str =~ s{/\*(?:[^*]+|\*+(?!/))*+\*/}{}g
这个想法是在交替中分离两种可能的情况:
- 所有不是
*
的字符
- 一个或多个
*
后面没有/
最后,只需要将包含交替的非捕获组的量词设为所有格即可。
但是交替和前瞻是有代价的。因此,另一种可能的方法是使用这种带有惰性量词的原子组的模式,而不是交替使用:
/\*(?>[^*]*\*+)+?/
请注意,它使用 +
量词来确保最终 *
的存在。
所以惰性量词对性能的影响非常有限(仅当满足一组*
时)。
即使第二种方式使用惰性量词,它也可能是一种更快的方式并且需要几个步骤来匹配整个评论。
注意: 在大多数语言中,未用 */
结束的 C 注释一直运行到文件末尾。让我们看看处理这种情况的两种模式的版本:
第一个模式:online demo(*)
/\*(?:[^*]+|\*+(?!/))*+(?:\*/|\z)
第二个模式:online demo(*)
/\*(?>[^*]*(?:\*+|\z(*ACCEPT)))+?/
(*)点击调试项查看每种方式所需的步骤数。