正则表达式学习总结
1 正则元字符
1.1 特殊单字符
. 任意字符,换行除外
\d 任意数字 \D 任意非数字
\w 任意字母数字下划线 \W 任意非字母数字下划线
\s 任意空白符 \S 任意非空白符
1.2 空白符
\r 回车符
\r 换行符
\r 换页符
\r 制表符
\r 垂直制表符
\s 任意空白符
1.3 量词
*含义:0到多次
+含义:1到多次
?含义:0到1次,如colou?r
{m}含义:出现m次
{m,}含义:出现至少m次
{m,n}含义:出现m到n次
1.4 范围
|或,如ab|bc代表ab或bc
[...]多选一,括号中任一单个元素
[a-z]匹配a到z之间的任意单个元素
[^...]取反,不能是括号中的任意单个元素
1.5 元字符和量词转换
元字符 | 同义表示方法 | 示例 |
---|---|---|
* | 0到多次 | |
+ | 1到多次 | |
? | 0到1次 |
2 正则三种模式
2.1 贪婪匹配
尽可能进行最长匹配
在正则中,表示次数的量词默认是贪婪的,在贪婪模式下,会尝试尽可能最大长度去匹配。
import re
re.findall(r'a*', 'aaabb') # 贪婪模式
['aaa', '', '', '']
2.2 非贪婪匹配
尽可能进行最短匹配
“数量”元字符后加?(英文)找出长度最小且满足要求的
import re
re.findall(r'a*?', 'aaabb') # 非贪婪模式
['', 'a', '', 'a', '', 'a', '', '', '']
2.3 独占模式
回溯:
不管是贪婪模式,还是非贪婪模式,都需要发生回溯才能完成相应的功能。但是在一些场景下,我们不需要回溯,匹配不上返回失败就好了,因此正则中还有另外一种模式,独占模式,它类似贪婪匹配,但匹配过程不会发生回溯,因此在一些场合下性能会更好。
regex="xy{1,3}z"
text="xyyz"
在匹配时,y{1,3}会尽可能长地去匹配,当匹配完 xyy 后,由于 y 要尽可能匹配最长,即三个,但字符串中后面是个 z 就会导致匹配不上,这时候正则就会向前回溯,吐出当前字符 z,接着用正则中的 z 去匹配。
量词后面加加号(+)
独占模式和贪婪模式很像,独占模式会尽可能多地去匹配,如果匹配失败就结束,不会进行回溯,这样的话就比较节省时间。具体的方法就是在量词后面加上加号(+)。
下面的例子匹配不到:
String str = "xyyz";
Pattern r = compile("xy{1,3}+yz");
Matcher m = r.matcher(str);
if(m.find()){
System.out.println(m.group());
}
3 分组与引用
3.1 分组
括号在正则中可以用于分组,被括号括起来的部分“子表达式”会被保存成一个子组,第几个括号就是第几个分组。
括号里面的会保存成子组,在括号里面使用:?就可以不保存子组;不保存子组可以提高正则的性能。
区分第几组:
只需要数左括号(开括号)是第几个,就可以确定是第几个子组。
命名分组:
后续更改正则会使编号发生变化,所以需要命名分组规则(?P<分组名>正则)。
在java语言中示例:
Pattern r = compile("(?<分组名>正则)");
3.2 分组引用
在知道了分组引用的编号 (number)后,大部分情况下,我们就可以使用 “反斜扛 + 编号”,即 \number 的方式来进行引用,而 JavaScript 中是通过$编号来引用,如$1。
编程语言 | 查找时引用方式 | 昔换时引用方式 |
---|---|---|
Python | \number如\1 | \number如\1 |
Java | \number如\1 | $number如$1 |
JavaScript | $number如$1 | $number如$1 |
示例:
regex="(\w+) \1"
text="cat cat"
4 四种匹配模式
4.1 不区分大小写模式
不区分大小写模式,它可以让整个正则或正则中某一部分进行不区分大小写的匹配。
(?i)正则
4.2 点号通配模式
点号通配模式也叫单行匹配,改变的是点号的匹配行为,让其可以匹配任何字符,包括换行。
(?s)正则
4.3 多行匹配模式
多行匹配说的是 ^ 和 $ 的匹配行为,让其可以匹配上每行的开头或结尾。
(?m)正则
4.4 注释模式
注释模式则可以在正则中添加注释,让正则变得更容易阅读和维护。
(?#word)
(\w+)(?#word) \1(?#word repeat again)
5 断言
5.1 单词边界
正则中使用\b 来表示单词的边界,放在前面是以正则开始,放在正则后面是以正则结束。
5.2 行的开始或结束
如果我们要求匹配的内容要出现在一行文本开头或结尾,就可以使用 ^ 和 $ 来进行位置界定。
使用场景:日志信息多行根据时间确定起始位置
数据校验,只能是6位数字结尾
>>> import re
>>> re.search('\d{6}', "1234567") is not None
True <-- 能匹配上 (包含6位数字)
>>> re.search('^\d{6}', "1234567") is not None
True <-- 能匹配上 (以6位数字开头)
>>> re.search('\d{6}$', "1234567") is not None
True <-- 能匹配上 (以6位数字结尾)
>>> re.search('^\d{6}$', "1234567") is not None
False <-- 不能匹配上 (只能是6位数字)
>>> re.search('^\d{6}$', "123456") is not None
True <-- 能匹配上 (只能是6位数字)
5.3 环视
只匹配固定长度的数据且前后都有限制,比如匹配六位数字的邮政编码,则7位数字就必须匹配失败。
左尖括号代表看左边,没有尖括号是看右边,感叹号是非的意思。
正则 | 名称 | 含义 | 示例 |
---|---|---|---|
(?<=Y) | 肯定逆序环视 | 左边是Y | (?<=\d)th能匹配到6th |
(?<!Y) | 否定逆序环视 | 左边不是Y | (?<!\d)th能匹配到health |
(?=Y) | 肯定顺序环视 | 右边是Y | six(?=\d)th能匹配到six8 |
(?!Y) | 否定逆序环视 | 右边不是Y | hi(?!\d)能匹配到high |