[英]Parse C-Style Comments with Regex, avoid Backtracking
我想匹配JavaScript文件中的所有块和多行注释(这些是C样式注释)。 我有一个运作良好的模式。 但是,它会创建一些回溯速度,从而显着降低速度,尤其是在较大的文件上。
模式: \\/\\*(?:.|[\\r\\n])*?\\*\\/|(?:\\/\\/.*)
示例: https : //www.regex101.com/r/pR6eH6/2
我怎样才能避免回溯?
由于交替,你有很大的回溯。 您可以考虑使用一个可以显着提升性能的字符类[\\s\\S]
,而不是(?:.|[\\r\\n])
。
\/\*[\s\S]*?\*\/|\/\/.*
见演示
在Python中,您可以使用re.S
/ re.DOTALL
修饰符来进行.
匹配换行符(注意单行注释模式应与\\/\\/[^\\r\\n]*
匹配):
/\*.*?\*/|//[^\r\n]*
但是 ,自*?
延迟量词也会导致类似于贪心量词引起的开销,你应该考虑为C风格多行注释使用更优化的模式 - /\\*[^*]*\\*+(?:[^/*][^*]*\\*+)*/
,整个正则表达式现在看起来像:
/\*[^*]*\*+(?:[^/*][^*]*\*+)*/|//.*
再看看另一个演示
细节 :
/\\*
- a /*
[^*]*
-比其他零个或多个字符*
\\*+
- 一个或多个星号 (?:[^/*][^*]*\\*+)*
- 零个或多个序列:
[^/*]
- 除/
和*
之外的符号 [^*]*
- 零以外的符号*
\\*+
- 1+星号 /
- 一个/
符号 |
- 要么 //.*
- //
以及除了换行符之外的任何0+字符。 只是想注意,在Python中,你不需要转义/
(在JS中,你不需要转义/
使用RegExp构造器声明正则表达式时)。
注意 :最后一个模式不允许简单捕获/*
和*/
,但由于模式比其他模式更稳定,我建议使用它,即使你需要捕获尾随的内容*
- /\\*([^*]*\\*+(?:[^/*][^*]*\\*+)*)/|//(.*)
- 然后你需要删除最后一个字符来自.group(1)
。
你能用你的模式做什么?
你的实际模式是:
\/\*(?:.|[\r\n])*?\*\/|(?:\/\/.*)
或者没有无用的反斜杠和组:
/\*(?:.|[\r\n])*?\*/|//.*
正如stribizhev所解释的那样(?:.|[^\\r\\n])*?
可以使用DOTALL模式以更简单的方式编写,即: .*?
或者不使用[\\s\\S]
代替点。
但是你可以做,如果你把在因素的第一个字符好得多/
这是常见的主交替(对于多行注释分支和单行注释的分支)的两个分支:
/(?:\*[\s\S]*?\*/|/.*)
这种变化的两个优点:
开始一个带有交替的模式并不是一个好主意,必须尽可能避免,因为正则表达式引擎必须测试字符串中每个位置的交替的两个分支(在最坏的情况下)。 所以在你的情况下(只有两个分支),你可以认为正则表达式引擎工作是X2。 如果你把第一个字符(或者更多的标记,如果可能的话)放在因子中,字符串中不感兴趣的位置的最大部分被更快地丢弃(不以/
开头的位置),因为只有一个分支要测试第一个角色不是好角色。
当您使用文字字符串启动模式时,正则表达式引擎能够使用更快的算法直接查找模式可能成功的字符串中的位置(文字字符串出现的位置)。 在您的情况下,使用此优化将使您的模式更快。
你可以改进的其他事情: 非贪婪的量词
非贪婪量词本质上是缓慢的(与贪心量词相比),因为每次取一个字符时,它必须测试模式的结尾是否成功(直到模式结束成功)。 换句话说,当回溯机制发生时,非贪婪的量词可能比贪婪的量词更糟糕(回溯机制以及量词的工作方式是更重要的(最重要的)重要事项之一,花时间去做) 。
您可以以更有效的方式重写子模式\\*[\\s\\S]*?\\*/
:
\*[^*]*\*+(?:[^*/][^*]*\*+)*/
细节:
\* # literal asterisk
[^*]* # zero or more character that are not an asterisk
\*+ # one or more asterisks: this one will match either the last asterisk(s)
# before the closing slash or asterisk(s) inside the comment.
(?:[^*/][^*]*\*+)* # In case there are asterisks(s) inside the comment, this
# optional group ensures the next character isn't a slash: [^*/]
# and reach the next asterisk(s): [^*]*\*+
/ # a literal slash
这个子模式更长,但效率更高,因为它只使用贪婪的量词,并且将回溯步骤减少到最小。
现在的模式是:
/(?:\*[^*]*\*+(?:[^*/][^*]*\*+)*/|/.*)
并且只需要~950步(而不是~12500)来查找示例字符串的63次出现。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.