DanLevy.net

正则表达式精通测验

你能驯服那些狂野的正则表达式吗?


准备好和正则表达式搏斗一番了吗?🤼‍♂️

用涵盖基本模式、量词、分组以及那些棘手的环视断言的问题,来测试你的正则知识。从简单的字符串匹配到复杂的模式验证——你能找出正确的正则表达式吗?

匹配什么?

'cat CAT Cat'.match(/cat/g)

这个模式使用了 g,但没有使用 i

  • g 查找所有匹配
  • 没有 i 标志时,匹配是区分大小写的

没有 i 标志,只有小写的 “cat” 会匹配。

这在处理用户输入或 HTML 时特别有用,因为大小写可能不同。

了解更多关于 RegExp 标志

这段代码会返回什么?

const words = ['cat', 'hat', 'what', 'bat'];
words.filter(word => word.match(/^[ch]at/))

模式 /^[ch]at/ 匹配的字符串需满足:

  • ^ 开头,且第一个字符是 ‘c’ 或 ‘h’([ch] 表示一个字符类,匹配一个字符)
  • 后面紧跟字面量 ‘at’

因此,只有 “cat” 和 “hat” 符合该模式。filter() 方法会保留所有匹配的元素。

在 MDN 上了解更多关于字符类的信息

这个模式会匹配到什么?

'<div>Hello</div><div>World</div>'.match(/<div>.*?<\/div>/g)

模式 /<div>.*?<\/div>/g 使用了非贪婪匹配,*? 表示:

  • 匹配 <div>
  • 匹配任意字符(.*),但尽可能少(?
  • 直到找到 </div>
  • g 标志使其匹配所有出现

如果没有 ?,贪婪的 .* 会从第一个 <div> 匹配到最后一个 </div>,得到一个大的匹配。加上 ? 后,它会分别匹配每一对。

了解更多关于贪婪与懒惰匹配

这将返回什么?

'hello\nworld'.match(/\w+/g)

\w+ 模式匹配一个或多个单词字符。尽管字符串中有换行符,\w 匹配:

  • 字母 (a-z, A-Z)
  • 数字 (0-9)
  • 下划线 (_)

因此,换行符充当了单词边界,我们得到两个匹配。如果我们使用 .*,默认情况下它不会匹配换行符(你需要 s 标志才能匹配)。

了解更多关于元字符的信息

这个模式会匹配到什么?

'$100 and €50'.match(/\d+(?=[\$€])/g)

这个模式不会匹配任何内容,因为先行断言用反了!如果你想要匹配前面有 $ 的数字,请使用后行断言:/(?<=[\$€])\d+/g

先行断言检查的是当前位置之后的内容。这个模式写的是:

  • 一个或多个数字(\d+
  • 后面跟着((?=...)$[\$€]

由于没有数字后面跟着货币符号(它们前面才有货币符号),所以没有匹配。

了解更多关于先行断言的信息

什么会匹配?

'cat cats category'.match(/\bcat\b/g)

\b 表示单词边界,它匹配:

  • 单词字符与非单词字符之间的位置
  • 字符串开头/结尾(如果该位置是单词字符)

因此 /\bcat\b/ 仅在 “cat” 是一个完整的单词时匹配它,而不是作为另一个单词的一部分。

  • ✅ “cat”(前后有空格)
  • ❌ “cats”(“cat” 后面没有边界)
  • ❌ “category”(“cat” 后面没有边界)

了解更多关于单词边界的信息

输出是什么?

'banana'.match(/a/g)

g(全局)标志改变了 match() 的行为:

  • 没有 g:返回第一个匹配项及其捕获组
  • g:返回所有匹配字符串的数组

在这个例子中,它找到 “banana” 中所有 “a” 的出现。

注意:如果你需要同时获取所有匹配项和捕获组,请使用 matchAll() 或循环中的 exec() 方法。

了解更多关于全局标志的信息

这个模式匹配什么?

'abc123 def456'.match(/(?<!abc)\d+/g)

负向后顾断言 (?<!abc) 确保数字前面不是 “abc”:

  • ❌ “123”(前面是 “abc”)
  • ✅ “23”(前面是 “abc1”)
  • ✅ “456”(前面是 “def”)

JavaScript 在现代引擎中支持后顾断言。此示例使用固定长度的后顾断言:abc 始终是三个字符。可变长度的后顾断言是更棘手的引擎特定边缘情况。

注意:后顾断言在 JavaScript 中相对较新。如果需要支持旧版浏览器,请查看浏览器兼容性

这将返回什么?

'2029-12-31'.match(/(\d{4})-(\d{2})-(\d{2})/).slice(1)

该模式使用了三个捕获组:

  1. (\d{4}) 捕获年份
  2. (\d{2}) 捕获月份
  3. (\d{2}) 捕获日期

不带 g 标志的 match() 返回:

  • 索引 0:完整匹配
  • 索引 1+:捕获组

slice(1) 是获取仅捕获组的常用技巧。

了解更多关于组和捕获的信息

以下代码的结果是什么?

"123aBc".match(/^\d+(?![a-z])/ig)

负向前瞻 (?![a-z]) 确保数字后面没有小写字母。由于 “3aBc” 部分在数字后面有一个小写字母,所以该部分不匹配。因此只有开头的 “12” 匹配。

了解更多关于负向前瞻

返回什么?

'a,b,c'.split(/(?<=,)/)

模式 /(?<=,)/ 是一个后顾断言,匹配逗号之后的位置:

  • a,(逗号之后)
  • b,(逗号之后)
  • c(后面没有逗号)

后顾断言不会消耗逗号,因此逗号仍然附着在分割结果的前一个片段上。

当你想要根据前面的内容分割字符串,同时保留分割字符时,这非常有用。

了解更多关于后顾断言的信息

匹配什么?

'$100'.match(/$\d+/)

特殊字符需要使用 \ 进行转义才能匹配字面量:

  • $ 是一个特殊字符(字符串结尾)
  • 要匹配字面量的美元符号,请转义它:\$

需要转义的常见字符:

. * + ? ^ $ [ ] \ ( ) { } |

如果不转义,许多特殊字符的正则表达式含义可能不是你想要的。

了解更多关于转义特殊字符的信息

匹配了什么?

'$100'.match(/(?<=\$)\d+/)

正向向后查找 (?<=\$) 确保数字前面有一个美元符号:

  • (?<=\$): 向后查找美元符号
  • \d+: 匹配一个或多个数字

向后查找断言不会消耗字符,它们只检查前面的内容。 当你想要根据前面的内容匹配某些内容而不包括前面的部分时,这很有用。

了解更多关于向后查找断言

匹配了什么?

'<b>bold</b>'.match(/<b>(.*?)<\/b>/).slice(1)

该模式使用 *? 进行懒惰匹配:

  • <b>:匹配开始标签
  • (.*?):捕获任意字符(懒惰)
  • </b>:匹配结束标签

* 后面的 ? 使其变为懒惰模式,尽可能少地匹配字符。 如果没有 ?,它将是贪婪模式,尽可能多地匹配。

slice(1) 返回捕获组的内容。

了解更多关于贪婪与懒惰匹配

匹配什么?

'😀 🙂'.match(/\p{Emoji}/gu)

u 标志启用:

  • Unicode 属性转义(\p{...}
  • 正确处理代理对

没有 u 标志,表情符号和其他 Unicode 字符可能无法正确匹配。 模式 \p{Emoji} 匹配具有 Unicode Emoji 属性的字符。在这个字符串中,这意味着两个表情符号图形。

注意:Unicode 属性转义需要 u 标志。

了解更多关于 Unicode 模式

提前道歉!😈
哪个密码匹配这个模式?

/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[!@#$%^&*]).{8,}$/

不要在生产中写这样的代码!😅

这个模式使用了多个正向先行断言来强制要求:

  • 至少一个大写字母:(?=.*[A-Z])
  • 至少一个小写字母:(?=.*[a-z])
  • 至少一个数字:(?=.*\d)
  • 至少一个特殊字符:(?=.*[!@#$%^&*])
  • 最小长度8个字符:.{8,}

先行断言非常适合密码验证,因为它们可以在不消耗字符的情况下检查多个条件。

了解更多关于密码验证模式的信息

你答得怎么样?🧐

正则表达式可能是个难驯的猛兽,但一旦你掌握了它(以及所有的新语法),它就会变得无比强大。继续练习,你很快就会成为正则大师!🧙‍♂️

做完这些正则题想歇口气?
哼,记住:技能 之后 再休息!

我的健身房再碾压几个挑战吧!💪