繁体   English   中英

用于匹配/替换 JavaScript 注释的正则表达式(多行和内联)

[英]RegEx for match/replacing JavaScript comments (both multiline and inline)

我需要使用 JavaScript RegExp ZA8CFDE6331BD59EB2ACZF6 从 JavaScript 源中删除所有 JavaScript 注释。

我需要的是 RegExp 的模式。

到目前为止,我发现了这个:

compressed = compressed.replace(/\/\*.+?\*\/|\/\/.*(?=[\n\r])/g, '');

此模式适用于:

/* I'm a comment */

或:

/*
 * I'm a comment aswell
*/

但似乎不适用于内联:

// I'm an inline comment

我不是 RegEx 及其模式的专家,所以我需要帮助。

另外,我想要一个 RegEx 模式,它可以删除所有那些类似 HTML 的注释。

<!-- HTML Comment //--> or <!-- HTML Comment -->

还有那些有条件的 HTML 评论,可以在各种 JavaScript 源中找到。

谢谢。

注意: Regex 不是 lexer 或 parser 如果您有一些奇怪的边缘情况,需要从字符串中解析出一些奇怪的嵌套注释,请使用解析器。 对于其他 98% 的时间,这个正则表达式应该可以工作。

我有非常复杂的块注释,带有嵌套的星号、斜杠等。以下站点的正则表达式就像一个魅力:

http://upshots.org/javascript/javascript-regexp-to-remove-comments
(原文见下文)

已经进行了一些修改,但保留了原始正则表达式的完整性。 为了允许某些双斜杠 ( // ) 序列(例如 URL),您必须在替换值中使用反向引用$1而不是空字符串 这里是:

/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm

// JavaScript: 
// source_string.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1');

// PHP:
// preg_replace("/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/m", "$1", $source_string);

演示: https ://regex101.com/r/B8WkuX/1

失败的用例:这个正则表达式有一些边缘情况失败。 这些案例的持续清单记录在此公共要点中。 如果您能找到其他案例,请更新要点。

...如果您还想删除<!-- html comments -->使用这个:

/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*|<!--[\s\S]*?-->$/

(原创——仅供历史参考)

// DO NOT USE THIS - SEE ABOVE
/(\/\*([\s\S]*?)\*\/)|(\/\/(.*)$)/gm

尝试这个,

(\/\*[\w\'\s\r\n\*]*\*\/)|(\/\/[\w\s\']*)|(\<![\-\-\s\w\>\/]*\>)

应该管用 :)在此处输入图像描述

我一直在拼凑一个需要做类似事情的表达式。
成品是:

/(?:((["'])(?:(?:\\\\)|\\\2|(?!\\\2)\\|(?!\2).|[\n\r])*\2)|(\/\*(?:(?!\*\/).|[\n\r])*\*\/)|(\/\/[^\n\r]*(?:[\n\r]+|$))|((?:=|:)\s*(?:\/(?:(?:(?!\\*\/).)|\\\\|\\\/|[^\\]\[(?:\\\\|\\\]|[^]])+\])+\/))|((?:\/(?:(?:(?!\\*\/).)|\\\\|\\\/|[^\\]\[(?:\\\\|\\\]|[^]])+\])+\/)[gimy]?\.(?:exec|test|match|search|replace|split)\()|(\.(?:exec|test|match|search|replace|split)\((?:\/(?:(?:(?!\\*\/).)|\\\\|\\\/|[^\\]\[(?:\\\\|\\\]|[^]])+\])+\/))|(<!--(?:(?!-->).)*-->))/g

可怕吧?

为了分解它,第一部分匹配单引号或双引号内的任何内容
这是避免匹配带引号的字符串所必需的

((["'])(?:(?:\\\\)|\\\2|(?!\\\2)\\|(?!\2).|[\n\r])*\2)

第二部分匹配由 /* */ 分隔的多行注释

(\/\*(?:(?!\*\/).|[\n\r])*\*\/)

第三部分匹配从行中任意位置开始的单行注释

(\/\/[^\n\r]*(?:[\n\r]+|$))

第四到第六部分匹配正则表达式文字中的任何内容
这依赖于前面的等号或正则表达式调用之前或之后的文字

((?:=|:)\s*(?:\/(?:(?:(?!\\*\/).)|\\\\|\\\/|[^\\]\[(?:\\\\|\\\]|[^]])+\])+\/))
((?:\/(?:(?:(?!\\*\/).)|\\\\|\\\/|[^\\]\[(?:\\\\|\\\]|[^]])+\])+\/)[gimy]?\.(?:exec|test|match|search|replace|split)\()
(\.(?:exec|test|match|search|replace|split)\((?:\/(?:(?:(?!\\*\/).)|\\\\|\\\/|[^\\]\[(?:\\\\|\\\]|[^]])+\])+\/))

我最初忘记的第七个删除了 html 注释

(<!--(?:(?!-->).)*-->)

我的开发环境出现问题,因为正则表达式断线而发出错误,所以我使用了以下解决方案

var ADW_GLOBALS = new Object
ADW_GLOBALS = {
  quotations : /((["'])(?:(?:\\\\)|\\\2|(?!\\\2)\\|(?!\2).|[\n\r])*\2)/,
  multiline_comment : /(\/\*(?:(?!\*\/).|[\n\r])*\*\/)/,
  single_line_comment : /(\/\/[^\n\r]*[\n\r]+)/,
  regex_literal : /(?:\/(?:(?:(?!\\*\/).)|\\\\|\\\/|[^\\]\[(?:\\\\|\\\]|[^]])+\])+\/)/,
  html_comments : /(<!--(?:(?!-->).)*-->)/,
  regex_of_doom : ''
}
ADW_GLOBALS.regex_of_doom = new RegExp(
  '(?:' + ADW_GLOBALS.quotations.source + '|' + 
  ADW_GLOBALS.multiline_comment.source + '|' + 
  ADW_GLOBALS.single_line_comment.source + '|' + 
  '((?:=|:)\\s*' + ADW_GLOBALS.regex_literal.source + ')|(' + 
  ADW_GLOBALS.regex_literal.source + '[gimy]?\\.(?:exec|test|match|search|replace|split)\\(' + ')|(' + 
  '\\.(?:exec|test|match|search|replace|split)\\(' + ADW_GLOBALS.regex_literal.source + ')|' +
  ADW_GLOBALS.html_comments.source + ')' , 'g'
);

changed_text = code_to_test.replace(ADW_GLOBALS.regex_of_doom, function(match, $1, $2, $3, $4, $5, $6, $7, $8, offset, original){
  if (typeof $1 != 'undefined') return $1;
  if (typeof $5 != 'undefined') return $5;
  if (typeof $6 != 'undefined') return $6;
  if (typeof $7 != 'undefined') return $7;
  return '';
}

这将返回引用的字符串文本捕获的任何内容以及在正则表达式文字中找到的任何内容,但会为所有注释捕获返回一个空字符串。

我知道这太过分了,而且很难维护,但到目前为止它似乎对我有用。

在简单的 JS 正则表达式中,这是:

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

这适用于几乎所有情况:

var RE_BLOCKS = new RegExp([
  /\/(\*)[^*]*\*+(?:[^*\/][^*]*\*+)*\//.source,           // $1: multi-line comment
  /\/(\/)[^\n]*$/.source,                                 // $2 single-line comment
  /"(?:[^"\\]*|\\[\S\s])*"|'(?:[^'\\]*|\\[\S\s])*'/.source, // - string, don't care about embedded eols
  /(?:[$\w\)\]]|\+\+|--)\s*\/(?![*\/])/.source,           // - division operator
  /\/(?=[^*\/])[^[/\\]*(?:(?:\[(?:\\.|[^\]\\]*)*\]|\\.)[^[/\\]*)*?\/[gim]*/.source
  ].join('|'),                                            // - regex
  'gm'  // note: global+multiline with replace() need test
);

// remove comments, keep other blocks
function stripComments(str) {
  return str.replace(RE_BLOCKS, function (match, mlc, slc) {
    return mlc ? ' ' :         // multiline comment (replace with space)
           slc ? '' :          // single/multiline comment
           match;              // divisor, regex, or string, return as-is
  });
}

该代码基于来自 jspreproc 的正则表达式,我为riot 编译器编写了这个工具。

http://github.com/aMarCruz/jspreproc

简单一点——

这也适用于多行 - (<!--.*?-->)|(<!--[\w\W\n\s]+?-->)

在此处输入图像描述

简单的正则表达式仅适用于多行:

/\*((.|\n)(?!/))+\*/

公认的解决方案并未涵盖所有常见用例。 请参阅此处的示例: https ://regex101.com/r/38dIQk/1。

以下正则表达式应该更可靠地匹配 JavaScript 注释:

/(?:\/\*(?:[^\*]|\**[^\*\/])*\*+\/)|(?:\/\/[\S ]*)/g

如需演示,请访问以下链接: https ://regex101.com/r/z99Nq5/1/。

["

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

如果您单击下面的链接,您会发现一个用正则表达式编写的评论删除脚本。

这些是 112 行代码,它们一起工作也适用于 mootools 和 Joomla 以及 drupal 和其他 cms 网站。 在 800.000 行代码和注释上对其进行了测试。 工作正常。 这一项还选择了多个括号(abc(/ nn /('/ xvx /'))“//测试行”)和冒号之间的注释并保护它们。 23-01-2016..! 这是带有注释的代码。!!!!!!

点击这里

我也在寻找一个快速的正则表达式解决方案,但没有一个答案能 100% 有效。 每个最终都会以某种方式破坏源代码,主要是由于在字符串文字中检测到的注释。 例如

var string = "https://www.google.com/";

变成

var string = "https:

为了那些来自谷歌的人的利益,我最终编写了一个简短的函数(在 Javascript 中),它实现了 Regex 无法做到的事情。 修改您用于解析 Javascript 的任何语言。

 function removeCodeComments(code) { var inQuoteChar = null; var inBlockComment = false; var inLineComment = false; var inRegexLiteral = false; var newCode = ''; for (var i=0; i<code.length; i++) { if (!inQuoteChar && !inBlockComment && !inLineComment && !inRegexLiteral) { if (code[i] === '"' || code[i] === "'" || code[i] === '`') { inQuoteChar = code[i]; } else if (code[i] === '/' && code[i+1] === '*') { inBlockComment = true; } else if (code[i] === '/' && code[i+1] === '/') { inLineComment = true; } else if (code[i] === '/' && code[i+1] !== '/') { inRegexLiteral = true; } } else { if (inQuoteChar && ((code[i] === inQuoteChar && code[i-1] != '\\') || (code[i] === '\n' && inQuoteChar !== '`'))) { inQuoteChar = null; } if (inRegexLiteral && ((code[i] === '/' && code[i-1] !== '\\') || code[i] === '\n')) { inRegexLiteral = false; } if (inBlockComment && code[i-1] === '/' && code[i-2] === '*') { inBlockComment = false; } if (inLineComment && code[i] === '\n') { inLineComment = false; } } if (!inBlockComment && !inLineComment) { newCode += code[i]; } } return newCode; }

2019:

所有其他答案都是不完整的,并且充满了缺点。 我花时间写完整的答案,工作

 function stripComments(code){ const savedText = []; return code .replace(/(['"`]).*?\1/gm,function (match) { var i = savedText.push(match); return (i-1)+'###'; }) // remove // comments .replace(/\/\/.*/gm,'') // now extract all regex and save them .replace(/\/[^*\n].*\//gm,function (match) { var i = savedText.push(match); return (i-1)+'###'; }) // remove /* */ comments .replace(/\/\*[\s\S]*\*\//gm,'') // remove <!-- --> comments .replace(/<!--[\s\S]*-->/gm, '') .replace(/\d+###/gm,function(match){ var i = Number.parseInt(match); return savedText[i]; }) } var cleancode = stripComments(stripComments.toString()) console.log(cleancode)


其他不适用于此类示例代码的答案:

// won't execute the creative code ("Can't execute code form a freed script"),
navigator.userAgent.match(/\b(MSIE |Trident.*?rv:|Edge\/)(\d+)/);

 function stripComments(code){ const savedText = []; return code // extract strings and regex .replace(/(['"`]).*?\1/gm,function (match) { savedText.push(match); return '###'; }) // remove // comments .replace(/\/\/.*/gm,'') // now extract all regex and save them .replace(/\/[^*\n].*\//gm,function (match) { savedText.push(match); return '###'; }) // remove /* */ comments .replace(/\/\*[\s\S]*\*\//gm,'') // remove <!-- --> comments .replace(/<!--[\s\S]*-->/gm, '') /*replace \ with \\ so we not lost \b && \t*/ .replace(/###/gm,function(){ return savedText.shift(); }) } var cleancode = stripComments(stripComments.toString()) console.log(cleancode)

我想知道这是否是教授给学生的一个诡计问题。 为什么? 因为在我看来,在一般情况下,使用正则表达式来做到这一点是不可能的。

您的(或任何人的代码)可以包含这样的有效 JavaScript:

let a = "hello /* ";
let b = 123;
let c = "world */ ";

现在,如果你有一个正则表达式删除一对 /* 和 */ 之间的所有内容,它会破坏上面的代码,它也会删除中间的可执行代码。

如果您尝试设计一个不会删除包含引号的评论的正则表达式,那么您将无法删除此类评论。 这适用于单引号、双引号和反引号。

您不能在 JavaScript 中使用正则表达式删除(所有)注释,在我看来,也许有人可以指出如何针对上述情况执行此操作。

您可以做的是构建一个小型解析器,它逐个字符地检查代码,并知道它何时在字符串内,何时在注释内,以及何时在字符串内的注释内等等。

我确信有很好的开源 JavaScript 解析器可以做到这一点。 也许一些打包和缩小工具也可以为您做到这一点。

对于块评论: https ://regex101.com/r/aepSSj/1

仅当斜线字符后跟星号时才匹配斜线字符( \1 )。

(\/)(?=\*)

可能后面跟着另一个星号

(?:\*)

其次是第一组比赛,或者从某事开始零次或多次......也许,不记得比赛,而是作为一个群体捕获。

((?:\1|[\s\S])*?)

其次是星号和第一组

(?:\*)\1

对于块和/或内联注释: https ://regex101.com/r/aepSSj/2

在哪里| mean or and (?=\/\/(.*))在任何之后捕获任何内容//

https://regex101.com/r/aepSSj/3也捕获第三部分

全部在: https ://regex101.com/r/aepSSj/8

演示: https ://onecompiler.com/javascript/3y825u3d5

const context = `
<html>
<script type="module">
/* I'm a comment */
/*
 * I'm a comment aswell url="https://example.com/"; 
*/
var re = /\\/*not a comment!*/; 
var m = /\\//.test("\"not a comment!\"");
var re = /"/; // " thiscommentishandledasascode!
const s1 = "multi String \\
    \\"double quote\\" \\
 // single commet in str \\
 /* multiple lines commet in str \\
    secend line */    \\
last line";

const s2 = 's2"s';
const url = "https://example.com/questions/5989315/";
let a = "hello /* ";
let b = 123;
let c = "world */ ";
//public static final String LETTERS_WORK_FOLDER = "/Letters/Generated/Work";

console.log(/*comment in 
    console.log*/ "!message at console.log");

function displayMsg(        // the end comment
    /*commet arg1*/ a, ...args) {
  console.log("Hello World!", a, ...args)
}
<\/script>
<body>
<!-- HTML Comment //--> or <!-- HTML Comment -->
<!--
function displayMsg() {
  alert("Hello World!")
}
//-->
</body>
</html>
`;
console.log("before:\n" + context);
console.log("<".repeat(100));
const save = {'txt':[], 'comment':[], 'regex': []};
const context2 = 
    context.replace(/(['"`]|\/[\*\/]{0,1}|<!\-\-)(?:(?=(?<=\/\*))[\s\S]*?\*\/|(?=(?<=\/\/)).*|(?=(?<=<!\-\-))[\s\S]*?\-\->|(?=(?<=[\s\=]\/)).+?(?<!\\)\/|(?=(?<=['"`]))[\s\S]*?(?<!\\)\1)/g,     
function (m) {
    const t = (m[0].match(/["'`]/) && 'txt') || (m.match(/^(\/\/|\/\*|<)/) && 'comment') || 'regex';
    save[t].push(m);
    return '${save.'+t+'['+(save[t].length - 1)+']}';
}).replace(/[\S\s]*/, function(m) {
    console.log("watch:\n"+m);
    console.log(">".repeat(100));
    /*
        @@remove comment
            save.comment = save.comment.map(_ => _.replace(/[\S\s]+/,""));
        @@replace comment
            save.comment = save.comment.map(_ => _.replace(/console\.log/g, 'CONSOLE.LOG'));
        @@replace text
            save.txt = save.txt.map(_ => _.replace(/console\.log/g, 'CONSOLE.LOG'));
        @@replace your code
        m = m.replace(/console\.log/g, 'console.warn');
    */
    // console.warn("@@remove comment -> save.comment.fill('');");
    save.comment.fill('');
    return m;
}).replace(/\$\{save.(\w+)\[(\d+)\]\}/g, function(m, t, id) {
    return save[t][id];
}).replace(/[\S\s]*/, function(m) {
    console.log("result:", m);
    // console.log("compare:", (context === m));
    return m;
})

我的英文不好,谁能帮忙翻译一下我写的东西,我将不胜感激

考虑一些问题

A. 注释中可能有字符串,或者字符串中有注释,比如

  1. /*
    常量 url="https://example.com/";

    */

  2. const str = "我是字符串和 /*commet in string*/";

B. 字符串中的 " 或 ' 或 ` 将被转义
喜欢

  1. const str = "我的名字是\"约翰\"";
  2. const str2 = '我是“约翰的”朋友';

结合上述多个正则表达式替换会导致一些问题考虑正则表达式查找到开头部分

 " ' ` // /* <!--

使用正则表达式

(['"`]|\/[\*\/]|<!\-\-)

(['"`]|/[*/]|<!\-\-) 结果为 \1

\1'"或其中之一

`

/*//<!--

在正则表达式中使用 If-Then-Else 条件

https://www.regular-expressions.info/conditional.html

(?:(?=(?<=\/\*))[\s\S]*?\*\/|(?=(?<=\/\/)).*|(?=(?<=<!\-\-))[\s\S]*?\-\->|[^\1]*?(?<!\\)\1)

如果(?=(?<=\/\*))[\s\S]*?\*\/

(?=(?<=\/\*)) positive lookbehind (?<=\/\*)因为/* 是多行注释,所以后面应该跟最新的 */

[\s\S]*?\*\/匹配完成 /* ..\n..\n. */ ..\n..\n. */

elseif (?=(?<=\/\/)).*

(?=(?<=//)).* positive lookbehind (?<=\/\/) catch // 单行注释

.*匹配完成 // any single commet

elseif (?=(?<=<!\-\-))[\s\S]*?\-\->

(?=(?<=<!--)) 正向后视(?<=<!\-\-)

[\s\S]*?\-\->匹配完成 <!-- ..\n..\n. /*/*\-\-> ..\n..\n. /*/*\-\->

否则[^\1]*?(?<!\\)\1

最后需要处理字符串

使用正则表达式[\s\S]*?\1

可能是 "STR\" 或 'STR"S\' 的错误结果

[\s\S]*? 我们可以使用“积极的向后看”

添加这个 [\s\S]*? (?<!\\) \1 过滤转义引号

结尾

对于 /**/ 和 //

/(?:(?:\/\*(?:[^*]|(?:\*+[^*\/]))*\*+\/)|(?:(?<!\:|\\\|\')\/\/.*))/gm

在此处输入图像描述

基于上述尝试并使用 UltraEdit ,主要是 Abhishek Simon,我发现这适用于内联评论并处理评论中的所有字符。

(\s\/\/|$\/\/)[\w\s\W\S.]*

这匹配行首的注释或//之前的空格

//public static final String LETTERS_WORK_FOLDER = "/Letters/Generated/Work";

但不是

"http://schemas.us.com.au/hub/'>" +

所以它只对像这样的东西不好

if(x){f(x)}//其中f是某个函数

它只需要

if(x){f(x)} //其中 f 是函数

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM