看懂火星文(二)

36次阅读

共计 3140 个字符,预计需要花费 8 分钟才能阅读完成。

感谢
本文参考《正则表达式迷你书》
位置匹配
什么是位置
位置就是空字符串。每一个字符不是位置, 正则中的位置可以理解如下

// 空字符串就是位置
“hello” == “” + “h” + “” + “e” + “” + “l” + “” + “l” + “” + “o” + “”;

“hello” == “” + “” + “hello”
如何匹配位置

位置
含义

^
匹配开头。多行匹配, 匹配行开头

$
匹配结尾。多行匹配, 匹配行结尾

b
单词边界, 可以是 w 与 W, w 与 ^, w 与 $ 之间的位置

B
非单词边界, w 与 w(字符与字符之间), W 与 W, ^ 与 W, $ 与 W 之间的位置

(?=p)
p 为一个子模式, (?=p)表示 p 字符前面的位置, (?=l)表示 l 字符前面的位置。换一种说法, 位置后面的字符需要是 p, 这个位置就是满足正则的位置

(?!p)
(?!p)是 (?=p) 的取反, 所有非 (?=p) 的位置。例如 (?!^) 所有非开头前面的位置。

^ 和 $
单行匹配
// “#Hello World#”
console.log(“Hello World”.replace(/^|$/g, ‘#’))
多行匹配
存在修饰符 n, ^$ 匹配的是每一行的开头和结尾。正则表达式需要添加 m 全局模式
// “#Hello#
// #World#”

console.log(‘Hello\nWorld’.replace(/^|$/gm, ‘#’))
b 和 B
b
b 匹配的都是单词边界

// []#I# #L#[] [#ove#] #you# #Fang#[]#Fang#!!!!

“[]I L[] [ove] you Fang[]Fang!!!!”.replace(/\b/g, ‘#’)

]I 为单词的边界
L[为单词的边界
[o 为单词的边界
e] 为单词的边界
 y 为单词的边界
u  为单词的边界
 F 为单词的边界
g[为单词边界
]F 为单词边界
g  为单词边界

B
B 匹配的是所有非单词边界, b 的取反
// #[#]I L[#]# #[o#v#e]# y#o#u F#a#n#g!#!#!#!#

“[]I L[] [ove] you F#a#n#g!!!!”.replace(/\B/g, ‘#’)
(?=p) 和 (?!p)
p 为子模式, p 的位置可以是其他的正则表达式
(?=p) 正向先行断言
// 所有空的字符前面添加 #
// p 前面总共有 3 个空字符, 所以添加 3 个 #
// ‘# # # p’
‘ p’.replace(/(?=[\s])/g, ‘#’)
(?!p) 负向先行断言
(?=p)位置的取反位置
// 所有非空字符的前面
// ‘ #p#’
‘ p’.replace(/(?![\s])/g, ‘#’)
案例
不匹配任何字符的正则

// . 表示通配符, 但是开头在却在字符的后面, 任何字符都不会满足

var reg = /.^/
千位分割符
可以使用 (?=) 正向先行断言, 配合 d{3}的子模式。(?=(d{3})$), 表示了从结尾开始, 后面有 3 个数字的位置, 因为不能包含结尾,所以不能是任意三个数字(123, 234 这种形式)。
因为我们需要匹配多组这种位置可以使用量词 +。(?=(d{3})+$)

“ 位置 ” + 123 + “ 位置 ” + 456 + $(结尾)

var reg = /(?=(\d{3})+$)/g

// “,123,456,789”
“123456789”.replace(reg, ‘,’)
这里我们遇到一个问题, 由于字符串开头 (^) 也符合正则的要求, 所以会导致这里也会添加一个逗号, 如何去除这个逗号呢? 有两种方式。第一种使用 B, 因为逗号必须添加在非单词边界中。第二种方式是使用负向先行断言(?!^), 要求了匹配的位置后面不能是开头。

var reg1 = /\B(?=(\d{3})+$)/g

// “123,456,789”
“123456789”.replace(reg1, ‘,’)

var reg2 = /(?!^)(?=(\d{3})+$)/g
// “123,456,789”
“123456789”.replace(reg2, ‘,’)

???? 题外话, 当初我在看书的解答前也思考了这个问题, 我的想法如下

var reg = /[^^](?=(\d{3})+$)/g

// “12,45,789”

“123456789”.replace(reg, ‘,’)
结果是 ”12,45,789″, 经过思考我发现自己陷入了一个思维的误区。我们这里需要匹配的是位置而为字符这很重要!
reg 的含义是, 后面跟有 3 个数字并且前面本身不是开头的字符, 而非位置, 从思维上区分位置和字符很重要
???? 正则表达式中匹配位置的只有 ^, $, b, B, (?=p), (?!p)

// ^ 表示的是位置
“123456789”.replace(/^/g, ‘,’)

// [^^]表示的是字符
“1 23456789”.replace(/[^^]/g, ‘,’)

如果此时字符串变成了 ”123456789 123456789″, 我们依然需要给字符中添加千位分割符, 我们又该如何做呢?
我们来分析下目前的情况, 这一个字符串中中间有一个 space, 我们这时不能继续使用 $, 因为如果使用 $, space 之前的字符将不能匹配, 因为无法满足(d{3})+ 子模式的条件。

我们可以将 $, 替换成 b, 表示从单词的边界开始, 前面三个数字之前的那个位置
// “,123,456,789 ,123,456,789”

“123456789 123456789”.replace(/(?=(\d{3})+\b)/g, ‘,’)
我们同时不能在单词的边界处添加逗号, 所以可以使用 B(非单词边界位置)或者(?!b)(非, 单词边界的前面, 的位置)

// “123,456,789 ,123,456,789”

“123456789 123456789”.replace(/\B(?=(\d{3})+\b)/g, ‘,’)

“123456789 123456789”.replace(/(?!\b)(?=(\d{3})+\b)/g, ‘,’)

???? 货币格式化
“1888” ==> “$ 1888.00”
var money = ‘1888’
var reg1 = /(?=(^))/g
var reg2 = /$/g

money = money.replace(reg1, ‘$ ‘)

// “$ 1888.00”
money = money.replace(reg2, ‘.00’)
验证密码
密码的验证规则, 6-12 位, 由数字小写字母和大写字母组成, 必须包含两种字符。
首先匹配第一个条件, 6-12 位

var reg = /^\w{6, 12}$/g
必须包含数字
// 表示是否存在, 包含数字的字符串, 前面的位置
var reg = /(?=.*[0-9])/g
必须包含小写字母, 必须包含大写字母同理

// 表示是否存在, 包含小写字母的字符串, 前面的位置
var reg = /?=.*[a-z]/g
我们将上面的正则组合

var reg = /((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[A-Z])|(?=.*[a-z])(?=.*[A-Z]))^\w{6,12}$/
// true
console.log(reg.test(‘123dde121’))
// true
console.log(reg.test(‘1231a311′))
// false
console.log(reg.test(’12z’))

另一种思路
包含两种类型的字符可以理解为不能全部是数字,不能全部是小写字母和不能全部是大写字母。?! 是?= 的区反。

// 是否存在,“非”全部是数字的, 前面的位置
var reg1 = /(?![0-9]{6,12})/
// 是否存在,“非”全部是小写字母的, 前面的位置
var reg2 = /(?![a-z]{6,12})/
// 是否存在,“非”全部是大写字母的,前面的位置
var reg3 = /(?![A-Z]{6,12})/

var reg = /(?![0-9]{6,12})|(?![a-z]{6,12})|(?![A-Z]{6,12})^\w{6,12}$/

正文完
 0