【问题标题】:Regex matching JS source that's not in a string or regex literal正则表达式匹配不在字符串或正则表达式文字中的 JS 源
【发布时间】:2012-04-30 00:55:36
【问题描述】:

是否存在全面的正则表达式,当应用于 JavaScript 源代码时,将匹配所有有效的字符串文字(例如 "say \"Hello\"")和正则表达式文字(例如 /and\/or/)?表达式必须涵盖所有边缘情况,包括换行符和转义序列。

或者,有人知道用于匹配字符串和正则表达式文字之外模式的正则表达式吗?

我的目标是实现一个简单的 JavaScript 语法扩展,允许预处理器扩展分隔符中的宏(例如 {{@foo.bar}}#@foo.bar#)。但是,我希望宏只在字面量的之外进行处理。

目前,我正在尝试仅使用字符串替换来完成此任务,而无需扩充现有的 JavaScript 词法分析器/解析器。

这个 JavaScript 预处理器本身将在 JavaScript 中实现。

【问题讨论】:

  • 我认为这对于正则表达式来说太过分了。考虑var foo = "//" // /"(?:\\.|[^"])*"/。字符串、cmets 和正则表达式文字在哪里开始和结束?
  • @TimPietzcker,我认为您的评论是正确答案。你无法在这里实现一个成熟的解析器。如果您发表评论作为答案,我会接受。 (我最终决定不使用宏,只使用特定的变量命名约定,所以我使用的是纯 JavaScript 语法。)

标签: javascript regex macros lexer


【解决方案1】:

这是我一直用来匹配带引号的字符串的正则表达式,它非常好,因为它几乎可以与所有引擎一起使用,因为它不需要回溯或反向引用或任何巫术。这将匹配所有文本 INSIDE 文字。

"(\\.|[^"])*"

根据引擎,它可能支持非捕获组。在这种情况下,您可以使用

"(?:\\.|[^"])*"

而且应该更快。

【讨论】:

  • +1,但最好在否定字符类 "(?:\\.|[^\\"])*" 中包含反斜杠,以防止像 "\\\" 这样的无效输入。此外,[\s\S] 可能应该替换点,因此将正确解析带有反斜杠转义换行符的多行字符串。当然,这个解决方案既不考虑 cmets 也不考虑正则表达式文字。当您可以在正则表达式文字中使用引号并在字符串中使用斜杠时,这会变得很棘手......
  • 是的,要完整,您需要实际解析脚本以了解实际字符串的内容。此正则表达式通常出现在词法分析的上下文中,您将有其他表达式优先确定它是否是注释。还有其他更长、更完整的,可能能够处理完整的场景。
  • /(["'#~\/])(\\\1|[^\1])*\1/g 是另一种选择,但这不会让开闭对发挥作用,例如 {{this}}[this]
【解决方案2】:

我认为这对于正则表达式来说太过分了。

考虑var foo = "//" // /"(?:\\.|[^"])*"/。字符串、cmets 和正则表达式文字在哪里开始和结束?您需要编写一个完整的 JavaScript 解析器来涵盖所有边缘情况。当然,解析器将使用正则表达式...

【讨论】:

    【解决方案3】:

    我可能会去做如下的事情。不过,它需要针对某些可能的情况进行改进。

    var str = '"aaa \"sss \\t bbb" sss #3 ss# ((t sdsds)) ff ';
    str += '/gg sdfd \/dsds/ {aaa bbb} {{ss}} {#sdsd#}';
    
    var repeating = ['"','\\\'','/','\\~','\\#'];
    // "example" 'example' /example/ ~example~ #example#
    var enclosing = [];
    enclosing.push(['\\{','\\}']);
    enclosing.push(['\\{\\{','\\}\\}']);
    enclosing.push(['\\[','\\]']);
    enclosing.push(['\\(\\(','\\)\\)']);
    // {example} {{example}} [example] ((example))
    
    for (var forEnclosing='',i = 0 ; i < enclosing.length; i++) {
        var e = enclosing[i];
        var r = e[0]+'(\\\\['+e[0]+e[1]+']|[^'+e[0]+e[1]+'])*'+e[1];
        forEnclosing += r + (i < enclosing.length-1 ? '|' : '');
    }
    for (var forRepeating='',i = 0; i < repeating.length; i++) {
        var e = repeating[i];
        var r = e+'(\\'+e+'|[^'+e+'])*'+e;
        forRepeating += r + (i < repeating.length-1 ? '|' : '');
    }
    
    var rx = new RegExp('('+forEnclosing+'|'+forRepeating+')','g');
    var m = str.match(rx);
    try { for (var i = 0; i < m.length; i++) console.log(m[i]) }
    catch(e) {}
    

    输出:

    "aaa "sss \t bbb"
    #3 ss#
    ((t sdsds))
    /gg sdfd /dsds/
    {aaa bbb}
    {{ss}}
    {#sdsd#}
    

    【讨论】:

      【解决方案4】:

      使用正则表达式最接近的方法是拥有一个匹配字符串文字(单引号或双引号)或正则表达式或注释(或任何其他可能包含虚假匹配项)或您的宏的一个正则表达式东西:

      "[^"\\]*(?:\\.[^"\\]*)*"
      |
      '[^'\\]*(?:\\.[^'\\]*)*'
      |
      /[^/\\]*(?:\\.[^/\\]*)*/[gim]*
      |
      /\*[^*]*(?:\*(?!/)[^*]*)*\*/
      |
      #@(\w+\.\w+)#
      

      如果第 1 组在比赛结束后包含任何内容,那么它一定是您要查找的内容。否则,忽略本场比赛,继续下一场比赛。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-06-05
        • 2013-12-25
        • 1970-01-01
        相关资源
        最近更新 更多