文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载本篇基于 Modern JavaScript Tutorialen.javascript.info正则表达式章节中的练习任务深入剖析用正则找出文本中所有 HTML 开闭标签含属性这一经典问题。你会理解为什么朴素的贪婪模式/./会一口吞掉多个标签、懒惰模式/.?/又为何会误伤空标签最终掌握用字符类排除法/[^]/写出既正确又高效的正则并顺带复习贪婪与懒惰量词的回溯原理。任务背景与约定练习原文4-find-html-tags-greedy-lazy/task.md要求创建一条正则表达式找出文本中所有 HTML 开标签与闭标签连同其属性。示例输入与期望输出如下let regexp /your regexp/g; let str a href/ input typeradio checked b; alert( str.match(regexp) ); // a href/, input typeradio checked, b注意两个关键细节字符串开头的并不是一个真正的标签它里面没有任何内容不该被匹配到任务做了一个简化假设标签属性值内包括引号内不包含和字符。这个假设直接决定了最终答案的形态稍后会看到它如何简化问题。为什么看起来很对的贪婪写法会失败第一次尝试直觉会写出用开头、.匹配任意字符、重复、收尾let regexp /./g;这是贪婪模式正则引擎会让.尽可能多地重复一直吞到字符串末尾再逐步回溯backtracking缩短匹配直到找到最后一个能让匹配成功的位置。于是从第一个到最后一个之间的所有内容都会被吞进同一次匹配let regexp /./g; let str a href/ input typeradio checked b; alert( str.match(regexp) ); // a href/ input typeradio checked b结果只有一个超大匹配把 4 个标签全串在了一起。这正是贪婪与懒惰量词章节里贪婪是万恶之源greediness is the cause of all evil的经典场景量词默认尽可能多重复.能匹配包括、在内的几乎任何字符所以它会跨越标签边界。贪婪引擎的幕后过程以该章节正文中的引号匹配为例可以看清这个回溯过程/ . /g匹配a witch and her broom is one时引擎先让.一路吃到字符串末尾然后发现后面没有引号了才开始逐字符回退直到回退到最后一个处才匹配成功于是得到witch and her broom这个串烧结果。HTML 标签问题里.吞掉是同一原理只是被回溯过程中恰好停下的位置而不是语义上正确的标签边界。懒惰模式问题解决了吗把量词改成懒惰形式.?试试let regexp /.?/g;懒惰模式下引擎每次只多吞一个字符就立刻检查是否能匹配因此匹配会尽早收尾。但在这个任务上它给出了错误结果let regexp /.?/g; let str a href/ input typeradio checked b; alert( str.match(regexp) ); // , a href/, input typeradio checked, b问题出在开头的懒惰的.?在之后只取一个字符后面的立即匹配成功于是把空标签也当成了合法标签。懒惰只保证尽早结束并不保证语义正确。此外懒惰模式还有性能与边界上的隐患它每前进一个字符都要做一次尝试匹配剩余模式的检查本质上是在做逐字符的回溯一旦属性值里真的出现本任务假设排除了这种情况它还会在错误的位置提前收尾。正确答案用字符类排除和方案的核心思路是不给量词任何越界的机会让中间部分只能匹配既不是也不是的字符。官方答案solution.md给出let regexp /[^]/g; let str a href/ input typeradio checked b; alert( str.match(regexp) ); // a href/, input typeradio checked, b逐段拆解这条正则片段含义匹配标签开头的左尖括号[^]字符类取反匹配任意不是或的字符属性名、、引号、属性值等都在此列上述字符至少出现一次匹配标签结尾的右尖括号g全局标志找出所有匹配它同时解决了前面的两个问题不会吞掉多个标签[^]遇到下一个或就必然停止绝不可能越过标签边界不会误匹配空标签要求中间至少有一个字符所以这种里面什么都没有的伪标签直接被排除没有回溯开销字符类匹配是确定性的引擎沿着字符串线性前进性能上优于依赖回溯的贪婪/懒惰方案。闭标签同样适用/b中间的内容是/b斜杠和字母既不是也不是所以/[^]/g对开标签与闭标签一视同仁。与章节其他练习的呼应这个任务的解法正是贪婪与懒惰量词章节末尾替代方案Alternative approach一节的实战化文中的[^]用排除引号的方式来替代懒惰量词.?与本任务的[^]是同一套思路。两者都说明一个结论——精细调校的贪婪搜索 排除字符类往往比懒惰量词更可靠。该章节中还给出了懒惰失效的著名反例用/a href.*? classdoc/g匹配两个链接时懒惰的.*?会一路偷看到后面p style classdoc处才凑齐 classdoc尾巴把无关内容卷入匹配改用href[^]*后问题消失。同一小节的姊妹练习可以帮你巩固两种模式的边界1-lazy-greedy\d? \d?匹配什么——结果不是直觉中的最小数字而是123 4第一个懒惰的\d?为了够到空格不得不吞下123第二个\d?只需要一个数字就够了。它说明懒惰是从最小开始、按需增长而不是固定取最小3-find-html-comments找出所有 HTML 注释——这里懒惰量词恰好是最优解/!--.*?--/gs配合s标志让.能跨行懒惰的.*?保证在最近的--处停止正确分离出!-- My -- comment \n test --与!----两条注释。对比可见懒惰量词与排除字符类各有所长。注释有明确、唯一的结束标记--懒惰写法最自然而标签的边界字符太常见属性值里随时可能出现就必须用排除法把边界焊死。小结方案写法结果贪婪/./g一个超大匹配吞掉所有标签懒惰/.?/g误匹配空标签且存在越界风险排除字符类推荐/[^]/g精确匹配所有开/闭标签忽略无回溯量词存在两种工作模式详见章节正文贪婪默认量词尽可能多地重复失败后再逐步回退缩短懒惰量词后加?每次重复前先尝试匹配剩余模式按需增长。而本任务给出第三条路用排除字符类限定可匹配的字符集合从根源上消除歧义。当你要匹配以某个字符为界、且边界字符可能出现在内容中的结构时[^边界字符]通常是比懒惰量词更稳、更快、更易读的选择。这也正是 Modern JavaScript Tutorial 在本节练习中希望读者掌握的正则思维先想清楚边界再选对量词。赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐HTML 标签正则匹配实战贪婪量词、懒惰量词与 [^] 排除字符集en.javascript.info 练习解析HTML 标签正则匹配实战贪婪量词、懒惰量词与 ^ 排除字符集en.javascript.info 练习解析 本篇文章基于 en.javascript.文档/教程前端用正则查找 HTML 注释贪婪与懒惰量词实战en.javascript.info用正则查找 HTML 注释贪婪与懒惰量词实战en.javascript.info HTML 注释以 ! 开头、以 结束但注释内容本身可能跨越多行、内部文档/教程前端Modern JavaScript Tutorial正则表达式中的贪婪与懒惰量词Greedy and Lazy Quantifiers完全指南Modern JavaScript Tutorial正则表达式中的贪婪与懒惰量词Greedy and Lazy Quantifiers完全指南 导读 在文档/教程前端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考