使用正则表达式在Javascript中删除HTML注释

45
我有一些从Word生成的丑陋HTML,我想剥离其中所有的HTML注释。
HTML的样子是这样的:
<!--[if gte mso 9]><xml> <o:OfficeDocumentSettings> <o:RelyOnVML/> <o:AllowPNG/> </o:OfficeDocumentSettings> </xml><![endif]--><!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:TrackMoves/> <w:TrackFormatting/> <w:HyphenationZone>21</w:HyphenationZone> <w:PunctuationKerning/> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:DoNotPromoteQF/> <w:LidThemeOther>NO-BOK</w:LidThemeOther> <w:LidThemeAsian>X-NONE</w:LidThemeAsian> <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript> <w:Compatibility> <w:BreakWrappedTables/> <w:SnapToGridInCell/> <w:WrapTextWithPunct/> <w:UseAsianBreakRules/> <w:DontGrowAutofit/> <w:SplitPgBreakAndParaMark/> <w:EnableOpenTypeKerning/> <w:DontFlipMirrorIndents/> <w:OverrideTableStyleHps/> </w:Compatibility> <m:mathPr> <m:mathFont m:val="Cambria Math"/> <m:brkBin m:val="before"/> <m:brkBinSub m:val="&#45;-"/> <m:smallFrac m:val="off"/> <m:dispDef/> <m:lMargin m:val="0"/> <m:rMargin m:val="0"/> <m:defJc m:val="centerGroup"/> <m:wrapIndent m:val="1440"/> <m:intLim m:val="subSup"/> <m:naryLim m:val="undOvr"/> </m:mathPr></w:WordDocument> </xml><![endif]-->

..而我正在使用的正则表达式是这个

html = html.replace(/<!--(.*?)-->/gm, "")

但是似乎没有匹配项,字符串保持不变。

我错过了什么?


4
可以的。请查看 http://jsfiddle.net/aQ5qp/。 - Chandu
整个字符串是注释,因此所有内容都被替换为 ""。 - Chandu
1
正如@Cybernate所说,正则表达式确实可以在该文本上工作,那么问题出在哪里呢?所有回答者都假设文本中有换行符,这可以解释问题,但我没有看到任何换行符。 - Alan Moore
1
https://dev59.com/X3I-5IYBdhLWcg3wq6do#1732454 - Iłya Bursov
2019年:我编写了一个很好的解决方案,可以删除JavaScript和HTML注释在这里 - pery mimon
显示剩余2条评论
7个回答

92

正则表达式/<!--[\s\S]*?-->/g应该可以工作。

您将会在CDATA块中删除转义文本跨度

例如:

<script><!-- notACommentHere() --></script>

在格式化的代码块中包含文字和字面量。
<xmp>I'm demoing HTML <!-- comments --></xmp>

<textarea><!-- Not a comment either --></textarea>

编辑:

这也不能防止新评论的引入,例如

<!-<!-- A comment -->- not comment text -->

应用该正则表达式一轮后,它将变成:

<!-- not comment text -->

如果这是一个问题,你可以转义那些不是注释或标签的<(这很难做到),或者像上面那样循环替换,直到字符串稳定下来。
这是一个正则表达式,可以匹配包括伪注释和未关闭的评论,符合HTML-5规范。 CDATA部分只允许在外部XML中使用。 这与上面的注意事项相同。
var COMMENT_PSEUDO_COMMENT_OR_LT_BANG = new RegExp(
    '<!--[\\s\\S]*?(?:-->)?'
    + '<!---+>?'  // A comment with no body
    + '|<!(?![dD][oO][cC][tT][yY][pP][eE]|\\[CDATA\\[)[^>]*>?'
    + '|<[?][^>]*>?',  // A pseudo-comment
    'g');

你会如何修改代码以仅获取评论并删除HTML? - guiomie
@guiomie,我不明白你的目标。能否请你详细解释一下? - Mike Samuel
1
@MikeSamuel,你最后的代码片段缺少CDATA周围的两个反斜杠转义符。 - Christian Semrau
1
请提供使用示例? - HerrimanCoder
1
这是一个不错的解决方案,谢谢。我正在尝试修改它以捕获跨越多行的注释。对此有什么想法吗?值得提一个新问题吗? - roman
显示剩余4条评论

10

这是基于 Aurielle Perlmann 的答案,它支持所有情况(单行、多行、未结束和嵌套注释):

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

这里

regex101 output


4

您应该使用/s修饰符。

html = html.replace(/<!--.*?-->/sg, "")

在Perl中测试过:

use strict;
use warnings;

my $str = 'hello <!--[if gte mso 9]><xml> <o:OfficeDocumentSettings> <o:RelyOnVML/> <o:AllowPNG/> </o:OfficeDocumentSettings> </xml><![endif]--><!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:TrackMoves/> <w:TrackFormatting/> <w:HyphenationZone>21</w:HyphenationZone> <w:PunctuationKerning/> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:DoNotPromoteQF/> <w:LidThemeOther>NO-BOK</w:LidThemeOther> <w:LidThemeAsian>X-NONE</w:LidThemeAsian> <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript> <w:Compatibility> <w:BreakWrappedTables/> <w:SnapToGridInCell/> <w:WrapTextWithPunct/> <w:UseAsianBreakRules/> <w:DontGrowAutofit/> <w:SplitPgBreakAndParaMark/> <w:EnableOpenTypeKerning/> <w:DontFlipMirrorIndents/> <w:OverrideTableStyleHps/> </w:Compatibility> <m:mathPr> <m:mathFont m:val="Cambria Math"/> <m:brkBin m:val="before"/> <m:brkBinSub m:val="&#45;-"/> <m:smallFrac m:val="off"/> <m:dispDef/> <m:lMargin m:val="0"/> <m:rMargin m:val="0"/> <m:defJc m:val="centerGroup"/> <m:wrapIndent m:val="1440"/> <m:intLim m:val="subSup"/> <m:naryLim m:val="undOvr"/> </m:mathPr></w:WordDocument> </xml><![endif]-->world!';

$str =~ s/<!--.*?-->//sg;
print $str;

输出:
你好,世界!


4
JavaScript没有s修饰符。请使用[\s\S]代替. - Mike Samuel

4

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

enter image description here


3

最近我需要做的事情是从HTML文件中删除所有注释。以下是其他答案未考虑到的一些事情:

  1. HTML文件可以有CSS和JS内联,我至少想要剥离掉。
  2. 在字符串或正则表达式中使用注释语法是完全有效的。(我的字符串/正则表达式排除模式基于:https://dev59.com/uGw15IYBdhLWcg3wqNqM#23667311

简而言之:(我只需要删除所有注释的正则表达式,请)

/\\\/|\/\s*(?:\\\/|[^\/\*\n])+\/|\\"|"(?:\\"|[^"])*"|\\'|'(?:\\'|[^'])*'|\\`|`(?:\\`|[^`])*`|(\/\/[\s\S]*?$|(?:<!--|\/\s*\*)\s*[\s\S]*?\s*(?:-->|\*\s*\/))/gm

这里有一个简单的演示:https://www.regexr.com/5fjlu


我不讨厌阅读,请继续:

我还需要进行各种其他匹配,考虑到包含其他有效目标的有效字符串。因此,我创建了一个类来处理我的多种用途。

class StringAwareRegExp extends RegExp {
    static get [Symbol.species]() { return RegExp; }

    constructor(regex, flags){
        if(regex instanceof RegExp) regex = StringAwareRegExp.prototype.regExpToInnerRegexString(regex);

        regex = super(`${StringAwareRegExp.prototype.disqualifyStringsRegExp}(${regex})`, flags);

        return regex;
    }

    stringReplace(sourceString, replaceString = ''){
        return sourceString.replace(this, (match, group1) => { return group1 === undefined ? match : replaceString; });
    }
}

StringAwareRegExp.prototype.regExpToInnerRegexString = function(regExp){ return regExp.toString().replace(/^\/|\/[gimsuy]*$/g, ''); };
Object.defineProperty(StringAwareRegExp.prototype, 'disqualifyStringsRegExp', {
    get: function(){
        return StringAwareRegExp.prototype.regExpToInnerRegexString(/\\\/|\/\s*(?:\\\/|[^\/\*\n])+\/|\\"|"(?:\\"|[^"])*"|\\'|'(?:\\'|[^'])*'|\\`|`(?:\\`|[^`])*`|/);
    }
});

根据这个,我创建了另外两个类来专注于我需要的两种主要类型的匹配:

class CommentRegExp extends StringAwareRegExp {
    constructor(regex, flags){
        if(regex instanceof RegExp) regex = StringAwareRegExp.prototype.regExpToInnerRegexString(regex);

        return super(`\\/\\/${regex}$|(?:<!--|\\/\\s*\\*)\\s*${regex}\\s*(?:-->|\\*\\s*\\/)`, flags);
    }
}

class StatementRegExp extends StringAwareRegExp {
    constructor(regex, flags){
        if(regex instanceof RegExp) regex = StringAwareRegExp.prototype.regExpToInnerRegexString(regex);

        return super(`${regex}\\s*;?\\s*?`, flags);
    }
}

最后(无论对谁有用)从这里创建的正则表达式是:
const allCommentsRegex = new CommentRegExp(/[\s\S]*?/, 'gm');
const enableBabelRegex = new CommentRegExp(/enable-?_?\s?babel/, 'gmi');
const disableBabelRegex = new CommentRegExp(/disable-?_?\s?babel/, 'gmi');
const includeRegex = new CommentRegExp(/\s*(?:includes?|imports?|requires?)\s+(.+?)/, 'gm');
const importRegex = new StatementRegExp(/import\s+(?:(?:\w+|{(?:\s*\w\s*,?\s*)+})\s+from)?\s*['"`](.+?)['"`]/, 'gm');
const requireRegex = new StatementRegExp(/(?:var|let|const)\s+(?:(?:\w+|{(?:\s*\w\s*,?\s*)+}))\s*=\s*require\s*\(\s*['"`](.+?)['"`]\s*\)/, 'gm');
const atImportRegex = new StatementRegExp(/@import\s*['"`](.+?)['"`]/, 'gm');

最后,如果有人想看它的使用情况。这是我用它做的项目(..我的个人项目总是在进行中..):https://github.com/fatlard1993/page-compiler


0
html = html.replace("(?s)<!--\\[if(.*?)\\[endif\\] *-->", "")

1
JavaScript不支持s修饰符,也不支持内联修饰符语法,如(?s) - Alan Moore

0

const regex = /<!--(.*?)-->/gm;
const str = `You will be able to see this text. <!-- You will not be able to see this text. --> You can even comment out things in <!-- the middle of --> a sentence. <!-- Or you can comment out a large number of lines. --> <div class="example-class"> <!-- Another --> thing you can do is put comments after closing tags, to help you find where a particular element ends. <br> (This can be helpful if you have a lot of nested elements.) </div> <!-- /.example-class -->`;
const subst = ``;

// The substituted value will be contained in the result variable
const result = str.replace(regex, subst);

console.log('Substitution result: ', result);


请解释一下为什么要使用 m 模式修饰符。 - mickmackusa

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