一、字符组
1.1
排除型
[^0-9] 表示 “0-9之外的字符“^”紧跟“[”表示“除什么什么之外”
排除型字符组只能排除单个字符
1.2 字符组简记
简记 | 对应字符 | 说明 |
---|---|---|
\d | [0-9] | digit |
\w | [0-9a-zA-Z_] | word |
\s | [\t\r\n\v\f] | \t制表符 \r回车符 \n换行符 |
\D | [^\d] | 与\d相反 |
\W | [^\w] | 与\w相反 |
\S | [^\s] | 与\s相反 |
[\s\S] [\d\D] [\s\S] | 匹配任意字符 |
二、量词
2.1 一般形式
量词 | 说明 |
---|---|
{n} | 之前的元素必须出现n次 |
{m,n} | 之前的元素最少出现m次,最多出现n次 |
{m,} | 最少出现m次,没有上限 |
{0,n} | 0到n次 |
2.2 量词简记法
常用量词 | {m,n}等价方式 |
---|---|
* | {0,} |
+ | {1,} |
? | {0,1} |
2.3 点号
点号匹配任意字符,其中\n不能匹配
2.4 回溯
"[^"]"主要为了排除"hello world" world"这种情况,
匹配优先量词
在拿不定是否要匹配的时候,优先尝试匹配,并且记下这个状态,以备将来后悔。
" . " 在匹配"hello world" second"时,会匹配第一个双引号之后的所有字符,再进行回溯,表达式中的"匹配了字符串的字符",整个匹配宣告完毕。
————————————————————
. 优先匹配,知道匹配最后一个字符,发现都是符合 . 这个状态的。
开始回溯。
最后一个字符符合,结束。
2.5 忽略优先量词
<script type="text/javascript">
alert('hello world');
</script>
<br/>
<script type="text/javascript">
alert("I think you're right");
</script>
匹配:
<script type="text/javascript">[/s/S]*</script>
匹配优先量词会优先匹配最后一个字符,这时候已经没有匹配的字符了,但正则表达式中的”</script>“还没有匹配到,所以只能查询之前保存备用的状态,看看能不能回退几步,照顾“</script>”的匹配。查询到最近保存的状态是:【“</script>”也可能是[/s/S]不应该匹配的】。于是让[/s/S]反悔对”</script>“的匹配,所以整个匹配过程宣告成功。
匹配的结果是:
alert('hello world');
</script>
<br/>
<script type="text/javascript">
alert("I think you're right");
这样和我们的预期并不相同。
我们需要采用忽略优先量词
如果不确定要匹配,忽略优先量词会选择“不匹配”的状态,再尝试表达式中之后的元素,如果尝试失败,再回溯,选择之前保存的“匹配”的状态。
也就是,当遇到”<“字符时,[/s/S]选择不匹配,尝试将“</script>”与表达式中的“</script>”比较,比较成功,返回成功的字符串。
正确的写法是
<script type="text/javascript">[/s/S]*?</script>
*与*?的区别在于,在实际匹配的过程中,遇到[/s/S]能匹配的字符,先尝试忽略,如果后面的元素不能匹配(即“</script>”),再尝试匹配。直到匹配到第一个出现的“</script>”为止。
匹配优先量词 和 忽略优先量词相比,匹配优先量词效率更高。
2.6 转义
一般量词的转义,例如{m,n},如果希望匹配{m,n},直接加上{m,n}即可
*? 的转义 是 /*/?
三、括号
3.1 分组
身份证号码
15位都是数字&首字母不能为0
18位 前17位是数字&首字母不能为0,末位可能是数字|字母x
说明 | 对应字符 |
---|---|
首位是数字,不能为0 | [1-9] |
去除首末2位,剩下13位或16位,都是数字 | \d{13, 16} |
末位可能是数字,也可能是x | [0-9x] |
即
[1-9]\d{13, 16}[0-9x]
但这样写是不对的,不能匹配。
我们可以采用括号的方式改变量词的作用元素
[1-9]\d{14}(\d{2}[0-9x])?
3.2 多选结构
结构形式为:
(...|...)
上面的身份证号可以表示为:
([1-9]\d{14}|[1-9]\d{16}[0-9x])
匹配0~255之间的数字
((00)?[0-9]|0?[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])
手机号
(0|\+86)?(13[0-9]|15[0-356]|18[025-9])\d{8}
四、断言
4.1 单词边界
说明:
表达式 | 说明 |
---|---|
\brow\b | 只能是单词row |
\brow | 例如row、rowdy等 |
row\b | 例如row、tomorrow等 |
用\b\w+\b来匹配一个句子中的单词。
4.2
^ 开始
$ 结束
4.3 环视
名字 | 记法 | 判断方向 | 结构内表达式匹配成功的返回值 |
---|---|---|---|
肯定顺序环视 | (?=...) | 向右 | True |
否定顺序环视 | (?!...) | 向右 | False |
肯定逆序环视 | (?<=...) | 向左 | True |
否定逆序环视 | (?<!...) | 向左 | False |
举个例子:
<(?!/) 表示<右侧不能出现/,它和<[^/]类似,但却不同。
<(?!/)匹配的仅仅只有<
<[^/]匹配<及其后面一个字符
环视最大的特点是“匹配完成之后还停留在原地”
格式化数字
(?<=\d)(?=(\d{3})+(?!\d))
'123456'.replace(/(?<=\d)(?=(\d{3})+(?!\d))/u, ',')
分析:
字符串可拆分为(?<=\d)和(?=(\d{3})+(?!\d))
(?<=\d)表示 当前字符左侧必须要出现数字
(?=(\d{3})+(?!\d))表示字符右侧必须要出现(\d{3})+(?!\d)
(\d{3})+(?!\d)可拆分
(\d{3})+表示三个连续数字至少出现1次
(?!\d)表示右侧必定不能出现数字
五、匹配模式
5.1
表达式 | 说明 |
---|---|
\regex\i | 不区分大小写 |
dotALL(点号通配) | 单行模式 |
\regex\m | 多行模式 |
\regex\x | 注释模式 |
\regex\u | 表示按unicode(utf-8)匹配(主要针对多字节比如汉字) |
\regex\s | 表示将字符串视为单行来匹配 |
六、捕获分组的引用
6.1
如果要在正则表达式内部引用捕获分组,则应当使用\num记法,其中num为对应捕获分组的编号
/^([a-z])\1$/u.test("aa") //true
如果要在replacement字符串中引用捕获分组,则应当使用$num记法。
"2019-10-28".replace(/(\d{4})-(\d{2})-(\d{2})/u, "$2/$3/$1") //10/28/2019
如需表示$符号,必须转义为$$
"the price is 12.99".replace(/(\d+\.\d{0,2})/u, "$$$1"); //the price is $12.99
6.2 命名分组
用法:
(?<name>regex)
栗子:
pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
result = pattern.exec("2019-10-30");
6.3(与6.1对应)
在正则表达式内部引用捕获分组,应当使用\k<name>记法
/^(?<char>[a-z])\k<char>$/u.test('aa');
regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
replacement = "$<month>/$<day>/$<year>"
"2019-10-30".replace(regex, replacement); //"10/30/2019"
"the price is 12.99".replace(/(?<money>\d+\.\d{0,2})/u, "$$$<money>");
七、