用于删除 JavaScript 注释的综合正则表达式

5
我需要一个可靠的正则表达式来移除所有JavaScript注释。我已经在StackOverflow和其他网站上搜索过,但没有考虑到交替引号、多行注释、字符串中的注释、正则表达式等情况。是否有任何正则表达式可以从以下内容中删除注释:
var test = [
    "// Code",
    '// Code',
    "'// Code",
    '"// Code',
    //" Comment",
    //' Comment',
    /* Comment */
    // Comment /* Comment
    /* Comment
     Comment // */ "Code",
    "Code",
    "/* Code */",
    "/* Code",
    "Code */",
    '/* Code */',
    '/* Code',
    'Code */',
    /* Comment
    "Comment",
    Comment */ "Code",
    /Code\/*/,
    "Code */"
]

这里有一个jsbin或者jsfiddle可以用来测试。


2
你这样做了吗?为什么?这个需求的背景是什么?你尝试过了吗? - David Thomas
你尝试过创建所需的正则表达式吗?如果是,请在此处发布。但请注意,仅使用正则表达式可能很难实现此任务。最好的做法是使用真正的JavaScript解析器。 - Amal Murali
3
高质量的小提琴加1分 - Lucas Trzesniewski
2
但是/Comment/gm可以工作:P - Lucas Trzesniewski
请查看 https://github.com/benjamn/recast。 - Felix Kling
显示剩余2条评论
5个回答

8

我喜欢挑战 :)

这是我的工作解决方案:

/((["'])(?:\\[\s\S]|.)*?\2|\/(?![*\/])(?:\\.|\[(?:\\.|.)\]|.)*?\/)|\/\/.*?$|\/\*[\s\S]*?\*\//gm

将其替换为$1

在这里进行实验:http://jsfiddle.net/LucasTrz/DtGq8/6/

当然,正如已经无数次指出的那样,一个合适的解析器可能会更好,但仍然......

NB:我在小提琴中使用了一个正则表达式文本而不是正则表达式字符串,太多的转义会毁掉你的大脑。


故障

((["'])(?:\\[\s\S]|.)*?\2|\/(?![*\/])(?:\\.|\[(?:\\.|.)\]|.)*?\/) <-- the part to keep
|\/\/.*?$                                                         <-- line comments
|\/\*[\s\S]*?\*\/                                                 <-- inline comments

保留的部分

(["'])(?:\\[\s\S]|.)*?\2                   <-- strings
\/(?![*\/])(?:\\.|\[(?:\\.|.)\]|.)*?\/     <-- regex literals

字符串

    ["']              match a quote and capture it
    (?:\\[\s\S]|.)*?  match escaped characters or unescpaed characters, don't capture
    \2                match the same type of quote as the one that opened the string

正则表达式字面量
    \/                          match a forward slash
    (?![*\/])                   ... not followed by a * or / (that would start a comment)
    (?:\\.|\[(?:\\.|.)\]|.)*?   match any sequence of escaped/unescaped text, or a regex character class
    \/                          ... until the closing slash

要删除的部分

|\/\/.*?$              <-- line comments
|\/\*[\s\S]*?\*\/      <-- inline comments

行注释

    \/\/         match two forward slashes
    .*?$         then everything until the end of the line

内联注释

    \/\*         match /*
    [\s\S]*?     then as few as possible of anything, see note below
    \*\/         match */

我必须使用[\s\S]代替.,因为不幸的是JavaScript不支持正则表达式s修饰符(singleline——它允许.匹配换行符)。
这个正则表达式将在以下情况下起作用:
- 包含/字符类的正则表达式模式:/[/]/ - 转义字符串文字中的换行符

最终BOSS战

仅仅为了好玩...这里还有刺眼的硬核版本:

/((["'])(?:\\[\s\S]|.)*?\2|(?:[^\w\s]|^)\s*\/(?![*\/])(?:\\.|\[(?:\\.|.)\]|.)*?\/(?=[gmiy]{0,4}\s*(?![*\/])(?:\W|$)))|\/\/.*?$|\/\*[\s\S]*?\*\//gm

这里添加了以下扭曲的边缘情况(fiddle, regex101):
Code = /* Comment */ /Code regex/g  ; // Comment
Code = Code / Code /* Comment */ /g  ; // Comment    
Code = /Code regex/g /* Comment */  ; // Comment

这是高度启发式的代码,你可能不应该使用它(甚至比之前的正则表达式更少),让那个边缘情况自行解决。

1
我会编辑答案以提供详细的分解,但现在它的意思是:匹配一个转义字符或未转义字符,而不捕获它。 - Lucas Trzesniewski
1
@Bergi 不, . 不应该匹配换行符,除非设置了s修饰符。在JS中不存在此修饰符(请参见答案底部的注释)。 孤立的 . 在这里不是问题。 - Lucas Trzesniewski
1
我的意思是字符串字面量可以包含换行符(如果转义),但是你的正则表达式无法匹配它们。 - Bergi
1
@Bergi 我忘了说:其实,谢谢你的吹毛求疵 :) - Lucas Trzesniewski
1
非常好!事实上,你最新的表达式也修复了我在第一个Windows中遇到的一个错误。第一个没有匹配\r\n,并且没有正确过滤我的文件。我用[\r|\n]修复了它,但当我回来告诉你时,你已经有了这个最终BOSS战斗!为所有辛勤工作加1分。 - wizulus
显示剩余10条评论

1

首先,我建议使用适当的JavaScript解析器来完成。请查看此前的问答:JavaScript parser in JavaScript

对于您提供的输入1,以下是一个可能有效的解决方案:

匹配该模式:

/("(?:[^\r\n\\"]|\\.)*"|'(?:[^\r\n\\']|\\.)*'|\/[^*\/]([^\\\/]|\\.)*\/[gm]*)|\/\/[^\r\n]*|\/\*[\s\S]*?\*\//g

这是一种模式的分解:

/
  (                                     # start match group 1
      "(?:[^\r\n\\"]|\\.)*"             #   match a double quoted string
    | '(?:[^\r\n\\']|\\.)*'             #   match a single quoted string
    | \/[^*\/]([^\\\/]|\\.)*\/[gm]*     #   match a regex literal
  )                                     # end match group 1
  | \/\/[^\r\n]*                        # match a single line break
  | \/\*[\s\S]*?\*\/                    # match a multi-line break
/g

并将其替换为$1(匹配组1)。这里的技巧是,除了注释之外的任何内容都在组1中匹配,它们会被再次替换为自身,但注释会被替换为空字符串。

这里有一个regexr演示,展示了以下替换:

  var test = [
      "// Code",
      '// Code',
      "'// Code",
      '"// Code',




       "Code",
      "Code",
      "/* Code */",
      "/* Code",
      "Code */",
      '/* Code */',
      '/* Code',
      'Code */',
       "Code",
      /Code\/*/,
      "Code */"
  ]

1 再次强调,使用解析器是最好的选择,因为正则表达式字面量可能会与除法运算符混淆。如果您在源代码中有类似于var x = a / b / g;的赋值语句,则上述解决方案将无法正常工作!


你似乎很清楚这个问题的困难之处 - 为什么还要建议使用正则表达式呢?这在很多情况下都会失败。 - Benjamin Gruenbaum
@BenjaminGruenbaum,只是为了好玩 :) - Bart Kiers
很好的回答 :) 看起来很有趣,你可能想建议使用esprima和escodegen来更加合理地在一行中删除注释。 - Benjamin Gruenbaum
@BenjaminGruenbaum,我不愿意推荐具体的JS解析器,因为我没有亲身体验过。但是我可以发布一篇关于JS解析器的以前问答文章。 - Bart Kiers
为什么单引号字符串可以包含换行符,而双引号字符串不行?(顺便说一下,'.' 不匹配转义的换行符)。此外,'/[/]/' 是一个有效的正则表达式。 - Bergi
@Bergi,忘记包含 \r\n。幸运的是 /[/]/ 没有出现在 OP 的测试集中!开玩笑的,我认为我的除法操作符示例应该可以吓走任何理智的人,不要在这里使用正则表达式(除了令人眼花缭乱的正则表达式“解决方案”...)。 - Bart Kiers

0

我建议您使用JavaScript解析器来解析JavaScript,并利用解析器API去除您不需要的部分。虽然我个人没有尝试过这样做,但正则表达式应该仅限于常规内容,而我怀疑JS是否属于此类。

以下是一些查找的好地方。

JavaScript中的JavaScript解析器


1
说句题外话,我非常确定字符串中的注释是一种常规语言,因为它们不能嵌套。 - Benjamin Gruenbaum

0
有没有正则表达式可以去除注释?
不行。你不能构建一个正则表达式来匹配注释(以便你可以用空字符串替换匹配项),因为没有前瞻,无法确定// "是注释还是字符串文字的结尾。
你可以使用正则表达式作为分词器(你只需要注意字符串文字、正则表达式文字和两种类型的注释),但我建议使用完整的JavaScript解析器,它们是免费提供的。

我对第一个“否定”并不十分确信,我相当确定可以根据正则表达式中的位置来确定//"是注释还是字符串字面量的结束符。此外,直观上,由于您只需要保存有限的东西,因此这应该是规则的 - 我肯定可以想象出一个DFA实现这一点。不过,你第二段说得很对。 - Benjamin Gruenbaum
我的意思是一个只匹配注释的正则表达式 - 这似乎是不可能的。当然,通过捕获组和智能替换函数来筛选注释但保留文字(类似于BartKiers所做的),这是可以实现的。 - Bergi

-1

test.replace(/(/*([\s\S]?)*/)|(//(.)$)/gm, '')


语法错误:非法的标记符号。 - wizulus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接