【问题标题】:Regex poor performance when nothing matches没有匹配项时正则表达式性能不佳
【发布时间】:2018-11-01 06:27:31
【问题描述】:

我遇到了运行缓慢的正则表达式的问题,但仅限于模式不匹配的情况。在所有其他情况下,性能是可以接受的,即使文本末尾的模式匹配。我正在测试 100KB 文本输入的性能。

我想要做的是将输入转换为使用 [] 而不是 括号的类似 HTML 的语法,并将其转换为有效的 XML。

示例输入:

...some content[vc_row param="test1"][vc_column]text [brackets in text] content[/vc_column][/vc_row][vc_row param="xxx"]text content[/vc_row]...some more content

样本输出:

...some content<div class="vc_row" param="test1"><div class="vc_column" >text [brackets in text] content</div></div><div class="vc_row" param="xxx">text content</div>...some more content

为此,我使用正则表达式:

/(.*)(\[\/?vc_column|\[\/?vc_row)( ?)(.*?)(\])(.*)/

我在 while 循环中执行此操作,直到模式匹配。

正如我之前提到的,这可行,但最后一次迭代非常慢(如果没有匹配项,则首先迭代)。这是我正在使用的完整 javascript:

var str   = '...some content[vc_row param="test1"][vc_column]text content[/vc_column][/vc_row][vc_row param="xxx"]text content[/vc_row]...some more content';

var regex = /(.*)(\[\/?vc_column|\[\/?vc_row)( ?)(.*?)(\])(.*)/;
while (matches = str.match(regex)) {
    matches = str.match(regex);
    if (matches[2].slice(1, 2) !== '/')
        str = matches[1] + "<div class=\"" + matches[2].slice(1) + "\"" + " " + matches[4] + ">" + matches[6];
    else
        str = matches[1] + "</div>" + matches[6];
}

如何提高正则表达式“不匹配”的性能?

【问题讨论】:

    标签: javascript regex html-parsing


    【解决方案1】:

    您可以将其拆分为 2 个正则表达式。 一个用于开始标签,一个用于结束标签。

    然后链 2 全局 g 替换。

    var str   = '...some content[vc_row param="test1"][vc_column]text with [brackets in text] content[/vc_column][/vc_row][vc_row param="xxx"]text content[/vc_row]...some more content';
    
    const reg1 = /\[(vc_(?:column|row))(\s+[^\]]+)?\s*\]/g;
    const reg2 = /\[\/(vc_(?:column|row))\s*\]/g;
    
    var result = str.replace(reg1, "<div class=\"$1\"$2>").replace(reg2, "</div>");
    
    console.log(result);

    请注意,这种方式不需要原始正则表达式中的(.*)。

    使用无名函数,然后可以通过 1 个正则表达式替换来完成。

    var str   = '...some content[vc_row param="test1"][vc_column]text with [brackets in text] content[/vc_column][/vc_row][vc_row param="xxx"]text content[/vc_row]...some more content';
    
    const reg = /\[(\/)?(vc_(?:column|row))(\s+[^\]]+)?\s*\]/g;
    
    var result = str.replace(reg, function(m,c1,c2,c3){
                  if(c1) return "</div>";
                  else return "<div class=\""+ c2 +"\""+ (c3?c3:"") +">";
                 });
    
    console.log(result);

    【讨论】:

    • 这对我有用,但我很好奇在你的答案@LukStorms 中使用 /s 的原因。我知道 /s 用于空格,但 ClasG 答案中的正则表达式不使用它,结果字符串是正确的,只是性能稍差。
    • @sss71 也许这样做更像是一种习惯(不仅仅是我)。当人们看到需要查找空格的要求时,使用 \s 可以做到这一点,并且还可以处理标签\t。但毕竟,这是一个示例,您可以根据需要对其进行调整。您可以简单地将 \s 替换为 " " 或 [ ](为了便于阅读)。但是,我确实想知道双重替换是否比具有该功能的替换更慢/更快。也可能是因为我的解决方案使用(vc_(?:column|row)) 而不仅仅是(vc_column|vc_row),所以回溯稍微少一些。 (如:什么没有“vc_”?下一个!)
    • 我对大量数据进行了性能测试,双重替换的速度大约是单次替换的两倍。 ClasG 示例(带函数)介于这两者之间,但更接近于双重替换。我已经使用 Javascript Performance API 对其进行了测量。
    • @sss71 感谢您的信息。两次获得 100k 是有代价的,这是有道理的。所以我有点希望它比 1 正则表达式慢。下次挑战时我会记住这一点。
    【解决方案2】:

    换一个怎么样...喜欢

    str.replace(/\[(\/?)(vc_column|vc_row)([^\]]*?)\]/g, function(a,b,c,d) {
        return '<' + b + 'div' + (b==='/' ? '' : ' class="' + c + '"') + d + '>';
        });
    

    这匹配一个标签(开始或结束)和所有属性,包括括号,捕获除括号之外的所有内容。然后以正确的格式重新组合在一起(divs 和 classes)。

    全局标志 (/../g) 消除了对任何循环的需要。

    var sInput = '...some content[vc_row param="test1"][vc_column]text [brackets in text] content[/vc_column][/vc_row][vc_row param="xxx"]text content[/vc_row]...some more content';
    
    console.log(sInput.replace(/\[(\/?)(vc_column|vc_row)([^\]]*?)\]/g, function(a,b,c,d) {
        return '<' + b + 'div' + (b==='/' ? '' : ' class="' + c + '"') + d + '>';
        })
        );

    【讨论】:

    • 感谢@ClasG 指出我使用函数作为替换的第二个参数。两个答案都对我有用,但 LukStorms 答案在我的测试中表现更好一些,拆分让我的眼睛更具可读性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-02
    • 1970-01-01
    • 2013-12-06
    • 1970-01-01
    • 2021-10-29
    相关资源
    最近更新 更多