首页 > 解决方案 > 使用正则表达式的大字符串的灾难性回溯问题

问题描述

我正在尝试捕获两个字符串之间的所有内容,问题是我要捕获的这个字符串可以长达 3000 行数字和逗号。因此,当发生这种情况时,我会遇到灾难性回溯的错误。

这是我正在使用的正则表达式,也是下面的示例数据

NEM12[\s\S]+?<\/CSVIntervalData>

<.CSVIntervalData>100,NEM12,201807290900,WBAYM,EEQ 200,3030910307,B1E1K1Q1,03,B1,N1,91111580,kWh,30, 300,20180728,.278,.278,.278,.278,.278, .278,.278,.278,.278,.278,.278,.278,.278,.056,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0,.074,.278,.278,.278,.278,.278,.278,.278,.278,.278,.278, .278,.278,.278,E75,,,20180729000320, 900 </CSVIntervalData>

请注意,中间可以有一千行数字、点和逗号

标签: regex

解决方案


您的正则表达式基于惰性匹配模式,如果您需要匹配的字符串很长,这意味着正则表达式引擎的大量开销。当NEM12匹配到时,</CSVIntervalData>会尝试,一旦引擎找不到它,它会扩展[\s\S]*?模式,匹配任何字符,然后再次重新测试</CSVIntervalData>模式,等等。一旦它多次执行,您可能会遇到问题(在 regex101,您通常会看到超时问题,而不是灾难性的回溯,因为这里没有使用惰性模式进行回溯,回溯仅由贪婪模式触发) .

你可以做的是解开惰性模式:

NEM12[^<]*(?:<(?!/CSVIntervalData>)[^<]*)*</CSVIntervalData>

请参阅正则表达式演示(注意 317 步与 46 步的区别)。

[\s\S]*?替换为[^<]*(?:<(?!/CSVIntervalData>)[^<]*)*: 0+ 以外的字符,然后是任何 0+不跟随的<序列,后跟任何 0+ 以外的字符。虽然它更长,但它匹配块中的文本,并且在预期匹配很长的情况下更快更可靠。如果您的文本在分隔符之间包含太多连续字符,则速度不会那么快,但实际数据通常并非如此。</CSVIntervalData><<

如果您需要捕获这两个字符串NEM12和之间</CSVIntervalData>的内容,请不要忘记捕获组:

NEM12([^<]*(?:<(?!/CSVIntervalData>)[^<]*)*)</CSVIntervalData>
     ^                                     ^    

请参阅此正则表达式演示


推荐阅读