Open libin1991 opened 5 years ago
标题挺有意思吧,一个来自正则,一个来自 CSS。
CSS
前者是正则断言,后者是 CSS 选择器。
正则是用来做什么的?匹配字符的。
选择器是用来做什么的?匹配元素的。
既然都是用来“匹配”的,那么,如果二者在一些地方有什么相似之处,应不足为奇。
我发现 (?<=p) 与 :nth-child() 就有很强的相似性。
(?<=p)
:nth-child()
这里容我慢慢道来。
本文假设读者对 CSS 选择器更熟悉些,所以下面的例子都是先 CSS,后正则。
假设页面上有 9 个 li,让所有元素的字都变成红色。此处不会使用 :nth-child 的,直接用标签选择器就行了。
9
li
:nth-child
li{ color: red; } 复制代码
'123456789'.replace(/./g, '*') // "*********" 复制代码
匹配首元素,在CSS 可以用 :first-child 选择器:
:first-child
li:first-child{ color:red; } 复制代码
^
'123456789'.replace(/^./g, '*') // "*23456789" 复制代码
而我们知道 :first-child 其实是 :nth-child 的特例:
li:nth-child(1){ color:red; } 复制代码
相应的,正则其实也可以用 (?<=p):
'123456789'.replace(/(?<=^)./g, '*') // "*23456789" 复制代码
(?<=^) 断言其实匹配的是一个位置,^ 之后的位置,当然还是开头。可以参考《JS正则迷你书》对位置的讲解。
(?<=^)
CSS 里要匹配第 3 个元素,:nth-child(3) 即可
3
:nth-child(3)
li:nth-child(3){ color:red; } 复制代码
2
'123456789'.replace(/(?<=^.{2})./g, '*') // "12*456789" 复制代码
我们知道 :nth-child 选择器厉害之处是在于它支持 an+b 表达式,比如匹配前 3 个:
an+b
li:nth-child(-n+3){ color:red; } 复制代码
0
'123456789'.replace(/(?<=^.{0,2})./g, '*') // "***456789" 复制代码
CSS 这边使用 2n+1:
2n+1
li:nth-child(2n+1){ color:red; } 复制代码
4
'123456789'.replace(/(?<=^(.{2})*)./g, '*') // "*2*4*6*8*" 复制代码
类似的匹配偶数位,即要匹配的字符前面有 1、3、5...个字符:
1
5
'123456789'.replace(/(?<=^(.)(.{2})*)./g, '*') // "1*3*5*7*9" 复制代码
比如 CSS 这边使用 4n+3
4n+3
li:nth-child(4n+3){ color:red; } 复制代码
正则这边变成了:
'123456789'.replace(/(?<=^(.{4})*.{2})./g, '*') // "12*456*89" 复制代码
即:要匹配的字符前面还有 4n+2 个字符
4n+2
我们知道 :nth-child 还有对应的 :nth-last-child。它的意思是,与 :nth-child 相反,不是从前往后数,而是从后面向前数,比如要匹配后 3 个 li:
:nth-last-child
li:nth-last-child(-n+3){ color:red; } 复制代码
p
(?=p)
'123456789'.replace(/.(?=.{0,2}$)/g, '*') // "123456***" 复制代码
更多的,与前几条类似,这里就不写了。
在 CSS 中,要匹配除了第 3 个元素之外的所有元素,可以配合使用 :not选择器来实现“补集”。
:not
li:not(:nth-child(3)){ color:red; } 复制代码
(?<!p)
'123456789'.replace(/(?<!^.{2})./g, '*') // "**3******" 复制代码
:nth-child 除了取补,还可以取交集,比如匹配第 3-7 个元素
7
li:nth-child(n+3):nth-child(-n+7){ color:red; } 复制代码
'123456789'.replace(/(?<=^.{2,})(?<=^.{0,6})./g, '*') // "12*****89" 复制代码
交并补,还有并集,CSS 很简单:
li:nth-child(3), li:nth-child(7){ color:red; } 复制代码
|
'123456789'.replace(/(?<=^(.{2}|.{6}))./g, '*') // "12*456*89" 复制代码
自此,这么一条条看下来,发现了二者确实有多相似之处。
这种跨界比较,我觉得很有趣!
本文完。
另外,欢迎继续阅读本人的《JS正则迷你书》。
标题挺有意思吧,一个来自正则,一个来自
CSS
。前者是正则断言,后者是
CSS
选择器。正则是用来做什么的?匹配字符的。
选择器是用来做什么的?匹配元素的。
既然都是用来“匹配”的,那么,如果二者在一些地方有什么相似之处,应不足为奇。
我发现
(?<=p)
与:nth-child()
就有很强的相似性。这里容我慢慢道来。
本文假设读者对
CSS
选择器更熟悉些,所以下面的例子都是先CSS
,后正则。1. 匹配所有元素
假设页面上有
9
个li
,让所有元素的字都变成红色。此处不会使用:nth-child
的,直接用标签选择器就行了。li{ color: red; } 复制代码
(?<=p)
。'123456789'.replace(/./g, '*') // "*********" 复制代码
2. 匹配第1个元素
匹配首元素,在
CSS
可以用:first-child
选择器:li:first-child{ color:red; } 复制代码
^
位置匹配符:'123456789'.replace(/^./g, '*') // "*23456789" 复制代码
而我们知道
:first-child
其实是:nth-child
的特例:li:nth-child(1){ color:red; } 复制代码
相应的,正则其实也可以用
(?<=p)
:'123456789'.replace(/(?<=^)./g, '*') // "*23456789" 复制代码
(?<=^)
断言其实匹配的是一个位置,^
之后的位置,当然还是开头。可以参考《JS正则迷你书》对位置的讲解。3. 匹配第3个元素
CSS
里要匹配第3
个元素,:nth-child(3)
即可li:nth-child(3){ color:red; } 复制代码
3
个,其实是说该字符前面还有2
个:'123456789'.replace(/(?<=^.{2})./g, '*') // "12*456789" 复制代码
4. 匹配前3个元素
我们知道
:nth-child
选择器厉害之处是在于它支持an+b
表达式,比如匹配前3
个:li:nth-child(-n+3){ color:red; } 复制代码
0
到2
个字符,'123456789'.replace(/(?<=^.{0,2})./g, '*') // "***456789" 复制代码
5. 匹配奇数位
CSS
这边使用2n+1
:li:nth-child(2n+1){ color:red; } 复制代码
0
、2
、4
...个字符,'123456789'.replace(/(?<=^(.{2})*)./g, '*') // "*2*4*6*8*" 复制代码
类似的匹配偶数位,即要匹配的字符前面有
1
、3
、5
...个字符:'123456789'.replace(/(?<=^(.)(.{2})*)./g, '*') // "1*3*5*7*9" 复制代码
6. 更一般的 an+b
比如
CSS
这边使用4n+3
li:nth-child(4n+3){ color:red; } 复制代码
正则这边变成了:
'123456789'.replace(/(?<=^(.{4})*.{2})./g, '*') // "12*456*89" 复制代码
即:要匹配的字符前面还有
4n+2
个字符7. (?=p) 与 :nth-last-child
我们知道
:nth-child
还有对应的:nth-last-child
。它的意思是,与:nth-child
相反,不是从前往后数,而是从后面向前数,比如要匹配后3
个li
:li:nth-last-child(-n+3){ color:red; } 复制代码
(?<=p)
表示p
后面的位置,与之相对的是(?=p)
,表示p
前面的位置。因此要匹配后3
个字符:'123456789'.replace(/.(?=.{0,2}$)/g, '*') // "123456***" 复制代码
更多的,与前几条类似,这里就不写了。
8. (?<!p) 与 :not(:nth-child())
在
CSS
中,要匹配除了第3
个元素之外的所有元素,可以配合使用:not
选择器来实现“补集”。li:not(:nth-child(3)){ color:red; } 复制代码
(?<=p)
表示p
后面的位置。而(?<!p)
有点绕,它表示所有位置中,不是p
后面的那个位置,或者说当下位置的前面不是p
。'123456789'.replace(/(?<!^.{2})./g, '*') // "**3******" 复制代码
9. :nth-child(n+3):nth-child(-n+7)
:nth-child
除了取补,还可以取交集,比如匹配第3
-7
个元素li:nth-child(n+3):nth-child(-n+7){ color:red; } 复制代码
(?<=p)
也可以支持交集的'123456789'.replace(/(?<=^.{2,})(?<=^.{0,6})./g, '*') // "12*****89" 复制代码
交并补,还有并集,
CSS
很简单:li:nth-child(3), li:nth-child(7){ color:red; } 复制代码
|
就是来做这个:'123456789'.replace(/(?<=^(.{2}|.{6}))./g, '*') // "12*456*89" 复制代码
自此,这么一条条看下来,发现了二者确实有多相似之处。
这种跨界比较,我觉得很有趣!
本文完。
另外,欢迎继续阅读本人的《JS正则迷你书》。