)
文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载MAC 地址Media Access Control address是网络接口的物理标识标准格式由 6 个以冒号分隔的两位十六进制数组成例如01:32:54:67:89:AB。本文基于现代 JavaScript 教程Modern JavaScript Tutorial即本仓库 en.javascript.info中《正则表达式捕获组》章节的习题 01-test-mac 任务及其官方题解完整讲解如何从零构造一个既能识别合法 MAC 地址、又能拒绝一切非法输入的 JavaScript 正则表达式。读完本文你将掌握「字符类 量词 捕获组 锚点」的组合套路以及RegExp.prototype.test在实际输入校验中的正确用法。任务要求验证整串输入是否为 MAC 地址原题task.md给出如下约束一个 MAC 地址由 6 个两位十六进制数组成数与数之间用冒号:分隔典型示例01:32:54:67:89:AB需要编写一个正则表达式使得regexp.test(str)对合法 MAC 地址返回true对以下非法输入返回false0132546789AB—— 没有冒号分隔01:32:54:67:89—— 只有 5 个数字段合法地址必须恰好 6 段01:32:54:67:89:ZZ—— 末尾出现非十六进制字符。注意这里的关键词是「检查check」即判断整个字符串是否符合格式而不是在长文本中「查找」出 MAC 地址。这一语义差别直接决定了锚点的使用稍后会详述。第一步定义单个十六进制字节[0-9a-f]{2}MAC 地址的每一段都是两位十六进制数。十六进制字符集为0-9与a-f大小写均可对应的字符类为[0-9a-f]。结合量词{2}两位十六进制数写作[0-9a-f]{2}这能匹配01、AB、ab、3f等任意两位十六进制组合。官方题解特别指出这里假设正则带有i标志[0-9a-f]{2} // 配合标志 i大小写均可标志i的含义是忽略大小写case-insensitive使A与a等价详见 01-regexp-introduction/article.md 的 Flags 一节。没有i时[0-9a-f]只能匹配小写a–f大写A–F会被判为非法因此i标志是让 MAC 校验大小写通用的必要前提。第二步用捕获组让量词作用于「冒号 数字段」整体MAC 地址由 6 段组成若直接写[0-9a-f]{2}:[0-9a-f]{2}:...重复六次模式会极其冗长。更优雅的做法是利用捕获组capturing group把模式的一部分用括号(...)包裹后括号后面的量词会作用于整个括号内容。回顾本章核心文章 11-regexp-groups/article.md 的定义模式的一部分可以用括号(...)括起来这被称为「捕获组」有两个作用允许把匹配的一部分作为独立条目放进结果数组如果在括号后放置量词量词将作用于括号整体。MAC 地址的结构可以拆解为首段[0-9a-f]{2}后面跟5 个:[0-9a-f]{2}。因此把「冒号 两位十六进制数」整体括起来并重复 5 次[0-9a-f]{2}(:[0-9a-f]{2}){5}解析[0-9a-f]{2}—— 第 1 段两位十六进制数(:[0-9a-f]{2}){5}—— 捕获组内容为「一个冒号后跟两位十六进制数」量词{5}令其整体重复 5 次对应第 26 段。这样模式恰好描述 1 5 6 个数字段{5}精确限定了数量因此01:32:54:67:89仅 5 段无法匹配。第三步用锚点^...$锁定整串匹配到此为止模式已经能匹配01:32:54:67:89:AB。但仅凭当前模式regexp.test(...01:32:54:67:89:AB...)前后有多余字符也会返回true因为test只要求字符串中存在匹配即可而不是整串完全符合。任务要求的是「检查一个字符串是否是 MAC 地址」即整个字符串必须从头到尾完全匹配。官方题解指出实现这一点的标准做法是用锚点把模式包裹起来^[0-9a-f]{2}(:[0-9a-f]{2}){5}$^匹配文本开头$匹配文本结尾。锚点成对出现^...$常用于「测试字符串是否完全匹配某格式」的场景例如校验用户输入是否合规详见 04-regexp-anchors/article.md 的 Testing for a full match 一节其中用^\d\d:\d\d$校验12:34格式的时间原理与本任务完全一致。官方题解也明确说明现在需要让匹配覆盖全部文本从开头开始、在结尾结束。这通过把模式包裹在^...$中实现。锚点属于「零宽度」断言它们不消费任何字符只是强制引擎检查「当前位置是否处于文本边界」因此不会干扰字符匹配逻辑只起到限定范围的作用。完整解答与逐项测试综合以上三步官方题解给出的最终正则如下let regexp /^[0-9a-f]{2}(:[0-9a-f]{2}){5}$/i; alert( regexp.test(01:32:54:67:89:AB) ); // true alert( regexp.test(0132546789AB) ); // false (no colons) alert( regexp.test(01:32:54:67:89) ); // false (5 numbers, need 6) alert( regexp.test(01:32:54:67:89:ZZ) ) // false (ZZ in the end)逐项剖析测试结果与正则的对应关系输入结果正则依据01:32:54:67:89:ABtrue6 段两位十六进制数冒号分隔大小写混合AB被i标志接受0132546789ABfalse缺少冒号无法匹配(:[0-9a-f]{2}){5}中要求的冒号结构01:32:54:67:89false只有 5 段{5}要求第 26 段全部存在且$锚点要求结尾恰好落在第 6 段之后01:32:54:67:89:ZZfalseZZ不在[0-9a-f]字符类内即使有i标志无法通过匹配值得强调的是最后一个用例ZZ会被[0-9a-f]拒绝同时$锚点也会拒绝任何「匹配之后还有多余字符」的情况。二者共同保证了输入要么是标准 MAC 地址、要么被整体拒绝。知识点纵深为什么这样写是「教科书级」的写法1. 捕获组 vs 直接重复(:...){5}背后的设计取舍有人可能疑惑既然每段都是[0-9a-f]{2}为什么不直接写[0-9a-f]{2}(:[0-9a-f]{2})重复六次或者干脆用{6}关键在于首段与后续段的语法结构不同——首段没有前置冒号后续 5 段各有前置冒号。因此模式被拆成「首段 5 次(冒号段)」用捕获组承载「冒号 十六进制对」这个重复单元是最简洁且无歧义的表达。这正是捕获组的核心用途之一让量词应用于一组字符的复合结构。2. 捕获组在结果数组中的位置虽然本例只用test做布尔判断但要知道(...)是捕获组若改用str.match(regexp)且不带g标志匹配结果数组中result[0]是完整匹配result[1]是最后一个(:[0-9a-f]{2})组的内容即第 6 段。组编号从左到右按左括号计数这一机制在 11-regexp-groups/article.md 的 Parentheses contents in the match 与 Nested groups 小节有完整演示。3. 若不想让括号「捕获」(?:...)非捕获组如果后续需要match返回更干净的结果、不想让冒号组占用数组下标可以把捕获组改为非捕获组(?:...)let regexp /^[0-9a-f]{2}(?::[0-9a-f]{2}){5}$/i;(?:...)仍能对量词进行分组但不计入捕获编号、不产生独立数组条目也不能在replace中通过$n引用参见 11-regexp-groups/article.md 的 Non-capturing groups with ?: 一节。在同章习题 04-parse-expression/solution.md 中正是用(?:\.\d)?来「保留分组、剔除捕获」。4. 锚点校验 vs 边界\b查找两种场景的分工注意区分两类需求整串校验本任务用^...$判断字符串整体是否符合格式文本内查找不加锚点必要时在尾部加词边界\b防止截断。同章习题 02-find-webcolor-3-or-6/solution.md 展示了一个经典反例用/#([a-f0-9]{3}){1,2}/gi在color: #3f3; background-color: #AA00ef; and: #abcd中查找颜色值时会误匹配#abc#abcd的前缀补上\b后/#([a-f0-9]{3}){1,2}\b/gi才得到正确结果。这从侧面印证「校验整串」与「在文本中查找」使用不同的边界策略MAC 校验属于前者必须用锚点而非\b。实战延伸从题解走向可复用工具函数将官方题解封装为可复用的校验函数并处理空值输入是生产环境的常见做法const MAC_RE /^[0-9a-f]{2}(:[0-9a-f]{2}){5}$/i; function isMacAddress(value) { return typeof value string MAC_RE.test(value); } console.log(isMacAddress(01:32:54:67:89:AB)); // true console.log(isMacAddress(01:32:54:67:89)); // false console.log(isMacAddress(null)); // false如需支持更多分隔形式如连字符-、点分格式可在不破坏整体结构的前提下扩展字符类例如^[0-9a-f]{2}([-:][0-9a-f]{2}){5}$/i思路与本解法完全一致先锁定最小重复单元再用量词与锚点约束数量和边界。深入阅读路径围绕本任务可在当前仓库中继续研读以下资料本章核心概念11-regexp-groups/article.md捕获组、嵌套组、可选组、matchAll、命名组(?name...)、替换中的$n与$name、非捕获组(?:...)锚点专题04-regexp-anchors/article.md^、$与整串匹配测试同章对比习题02-find-webcolor-3-or-6/solution.md量词作用于组的查找场景、04-parse-expression/solution.md非捕获组与命名组的工程化用法正则基础01-regexp-introduction/article.md两种字面量语法与全部 6 个标志的含义。总结校验 MAC 地址的正则^[0-9a-f]{2}(:[0-9a-f]{2}){5}$/i由三个独立的知识点拼装而成字符类[0-9a-f]配合i标志定义十六进制字节捕获组(:[0-9a-f]{2})配合量词{5}精确描述「冒号 十六进制对」的 5 次重复锚点^...$把匹配范围锁定为整个字符串。三者各司其职缺一不可——这也是所有「格式校验型」正则的通用设计范式先定义原子单元再借助分组与量词表达重复结构最后用锚点约束整体边界。赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐JavaScript 正则表达式中的捕获组详解JavaScript 正则表达式中的捕获组详解 正则表达式是 JavaScript 中强大的文本处理工具而捕获组则是正则表达式中一个非常重要的概念。本文将深入文档教程前端JavaScript 正则表达式中的捕获组详解JavaScript 正则表达式中的捕获组详解 正则表达式是 JavaScript 中处理字符串的强大工具而捕获组 capturing groups 则是正则JavaScript 正则表达式中的捕获组详解JavaScript 正则表达式中的捕获组详解 正则表达式是 JavaScript 中处理字符串的强大工具而捕获组 capturing groups 则是正则文档/教程前端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考