正则表达式(可以称为REs,regex,regex pattens)是一个小巧的,高度专业化的编程语言,它内嵌于python开发语言中,可通过re模块使用。正则表达式的pattern可以被编译成一系列的字节码,然后用C编写的引擎执行。
常用正则表达式符号,基本上,包含了90%的场景。
'.' | 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行 |
'^' | 匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","\nabc\neee",flags=re.MULTILINE) |
'$' | 匹配字符结尾,或e.search("foo$","bfoo\nsdfsf",flags=re.MULTILINE).group()也可以 |
'*' | 匹配*号前的字符0次或多次,re.findall("ab*","cabb3abcbbac") 结果为['abb', 'ab', 'a'] |
'+' | 匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb'] |
'?' | 匹配前一个字符1次或0次 |
'{m}' | 匹配前一个字符m次 |
'{n,m}' | 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb'] |
'|' | 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC' |
'(...)' | 分组匹配,re.search("(abc){2}a(123|456)c", "abcabca456c").group() 结果 abcabca456c |
'\A' | 只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的 |
'\Z' | 匹配字符结尾,同$ |
'\d' | 匹配数字0-9 |
'\D' | 匹配非数字 |
'\w' | 匹配[A-Za-z0-9] |
'\W' | 匹配非[A-Za-z0-9] |
's' | 匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 结果 '\t' |
'(?P<name>...)' | 分组匹配 |
最常用的匹配语法
re.match 从头开始匹配
re.search 匹配包含
re.findall 把所有匹配到的字符放到以列表中的元素返回
re.splita 以匹配到的字符当做列表分隔符
re.sub 匹配字符并替换
反斜杠的困扰
与大多数编程语言相同,正则表达式里使用"\"作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符"\",那么使用编程语言表示的正则表达式里将需要4个反斜杠"\\\\":前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用r"\\"表示。同样,匹配一个数字的"\\d"可以写成r"\d"。有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
举个列子:
匹配以Chen开头的字符串
import re
result = re.match("^Chen","ChenLong")
print(result)
执行输出
_sre.SRE_Match object; span=(0, 4), match='Chen'
结果是一个匹配对象,请注意结尾的match='Chen' 表示匹配出了Chen
如果没有匹配上,结果为None
打印匹配结果,使用group()方法查看
print(result.group())
执行输出 Chen
注意:如果没有匹配上,使用group()会报错。
上面的正则匹配规写死了,比如^Chen 这种需求,用in方法就可以实现了。
下面说一个简单的例子
匹配以Chen开头的以及后面的数字
import re
result = re.match("^Chen\d","Chen356Long")
print(result.group())
执行输出
Chen3
注意:
\d 表示匹配一个数字
如果想要匹配多个数字,使用\d+
import re
result = re.match("^Chen\d+","Chen356Long")
print(result.group())
执行输出
Chen356
匹配任意字符.+
res = re.match(".+","Chen321Long123")
print(res.group())
执行输出
Chen321Long123
匹配单个字符.
res = re.match(".","Chen321Long123")
print(res.group())
执行输出: C
匹配Long
res = re.match("^L.+g","Chen321Long123")
print(res)
执行输出: None
为什么呢?因为match是从左至右匹配,由于Long在字符串的中间,写任何正则都无法匹配出Long。
需要用到另外一个方法search,表示从整个文本中去搜索。结果只会返回一次,如果有多个结果,会返回第一个结果。
res = re.search("L.+g","Chen321Long123")
print(res.group())
执行输出: Long
如果使用L.+g$ 是匹配不到Long的,为什么呢?
$表示匹配整个字符串的结尾,而结尾是3。由于123不是我想要的,所以不能写g$
由于.+是匹配任意字符,如果只想匹配字母呢?使用[a-z]
res = re.search("L[a-z]+g","Chen321Long123")
匹配所有字母大小写呢?使用[a-zA-Z]
res = re.search("L[a-zA-Z]+g","Chen321Long123")
匹配jack
res = re.search("[a-z]+k","123#tom#jack#rose")
print(res)
执行输出:
_sre.SRE_Match object; span=(8, 12), match='jack'
'?' 匹配前一个字符1次或0次
匹配字母a
res = re.search("a?","alin")
print(res)
执行输出:
_sre.SRE_Match object; span=(0, 1), match='a'
匹配字母a
res = re.search("a?","lina")
print(res)
执行输出:
_sre.SRE_Match object; span=(0, 0), match=''
结果显示没有匹配上,请注意,? 可以匹配0次,也就是不匹配的情况。所以它的结果不是None
'?' 匹配前一个字符1次或0次
匹配aa或者aaa
res = re.search("aaa?","aalinaaa")
print(res)
执行输出:
_sre.SRE_Match object; span=(0, 2), match='aa'
请注意aaa? 需要拆分一下aa和aaa? 为什么呢? '?'是匹配0次或者1次数
aaa?匹配0次就是aa,匹配1次,就是aaa?
'{m}' 匹配前一个字符m次
匹配3个数字
res = re.search("[0-9]{3}","aa1x2a345aa")
print(res)
执行输出:
_sre.SRE_Match object; span=(6, 9), match='345'
匹配1到3次
res = re.search("[0-9]{1,3}","aa1x2a345aa")
print(res)
执行输出:
_sre.SRE_Match object; span=(2, 3), match='1'
匹配所有数字
res = re.search("[0-9]+","aa1x2a345aa")
print(res)
执行输出:
_sre.SRE_Match object; span=(2, 3), match='1'
为什么只有一个1呢?因为search只会返回一个结果,后续的不再返回。这个时候,需要用到findall方法
注意:findall没有group()方法
res = re.findall("[0-9]+","aa1x2a345aa")
print(res)
执行输出:
['1', '2', '345']
所有的数字,都匹配出来了。
只匹配第3次的结果
res = re.findall("[0-9]{3}","aa1x2a345aa")
print(res)
执行输出:
['345']
'|' 匹配|左或|右的字符
匹配abc或者ABC
res = re.findall("abc|ABC","ABCBabcCD")
print(res)
执行输出:
['ABC', 'abc']
'(...)'分组匹配
匹配abc,在匹配c 2次
res = re.search("abc{2}","xiabccc")
print(res)
执行输出:
_sre.SRE_Match object; span=(2, 6), match='abcc'
复杂的例子
匹配abc 2次,匹配||= 2次。注意:\| 转义了,表示|
res = re.search("(abc){2}(\|\|=){2}","abcabc||=||=")
print(res)
执行输出:
_sre.SRE_Match object; span=(0, 12), match='abcabc||=||='
'\A'只从字符开头匹配
'\Z'匹配字符结尾,同$
'\A' 效果和'^' 是一样的。
匹配以数字开头,字母结尾
res = re.search("\A[0-9]+[a-z]\Z","123a")
print(res)
执行输出:
_sre.SRE_Match object; span=(0, 4), match='123a'
'\d' 匹配数字
res = re.search("\A\d+[a-z]\Z","123a")
print(res)
执行输出:
_sre.SRE_Match object; span=(0, 4), match='123a'
'\D' 匹配非数字
匹配非数字
res = re.search("\D+","123a$ -\n")
print(res)
执行输出:
_sre.SRE_Match object; span=(3, 8), match='a$ -\n'
'\w'匹配[A-Za-z0-9]
res = re.search("\w+","1dF23$- \r\na")
print(res)
执行输出:
_sre.SRE_Match object; span=(0, 5), match='1dF23'
'\W'匹配非[A-Za-z0-9]
res = re.search("\W+","1dF23$- \r\na")
print(res)
执行输出:
_sre.SRE_Match object; span=(5, 10), match='$- \r\n'
's'匹配空白字符、\t、\n、\r
res = re.search("\s+","1dF23$- \r\na")
print(res)
执行输出:
_sre.SRE_Match object; span=(7, 10), match=' \r\n'
'(?P<name>...)' 分组匹配
组名为id,匹配数字
res = re.search("(?P<id>[0-9]+)","abcd1234daf@34")
#使用groupdict()方法打印组名
print(res.groupdict())
执行输出:
{'id': '1234'}
再添加一个分组name,匹配字母大小写
res = re.search("(?P<id>[0-9]+)(?P<name>[a-zA-Z]+)","abcd1234daf@34")
print(res.groupdict())
执行输出:
{'name': 'daf', 'id': '1234'}
返回的结果是一个字典,如果想取值的话,使用如下方法:
res = re.search("(?P<id>[0-9]+)(?P<name>[a-zA-Z]+)","abcd1234daf@34")
print(res.groupdict())
#第一种方法,直接传值
print(res.group("id"))
#第二种方法,用字典的方式
print(res.groupdict()['name'])
执行输出:
{'id': '1234', 'name': 'daf'}
1234
daf
举个复杂的例子
身份证号,前2位是省,再后面2位是市,再后面2位是区,再后面8位是出生日期
res = re.search("(?P<province>[0-9]{2})(?P<city>[0-9]{2})(?P<area>[0-9]{2})(?P<birthday>[0-9]{4})","371481199306143242").groupdict("city")
print(res)
执行输出:
{'birthday': '1993', 'city': '14', 'province': '37', 'area': '81'}
re.split 以匹配到的字符当做列表分隔符
res = re.split("[0-9]","abc12de3f45GH")
print(res)
执行输出:
['abc', 'de', 'f', 'GH']
re.sub 匹配字符并替换
res = re.sub("[0-9]+","|","abc12de3f45GH")
print(res)
执行输出:
abc|de|f|GH
只替换一个
res = re.sub("[0-9]+","|","abc12de3f45GH",count=1)
print(res)
执行输出:
abc|de3f45GH
仅需轻轻知道的几个匹配模式
re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同)
M(MULTILINE): 多行模式,改变'^'和'$'的行为(参见上面)
S(DOTALL): 点任意匹配模式,改变'.'的行为
忽略大小写
res = re.search("[a-z]+","abcA",flags=re.I)
print(res)
执行输出:
_sre.SRE_Match object; span=(0, 4), match='abcA'
res = re.search(r"^a","\nabc\neee",flags=re.M)
print(res)
执行输出:
_sre.SRE_Match object; span=(1, 2), match='a'
匹配任意字符
res = re.search(r".+","\nabc\neee",flags=re.S)
print(res)
执行输出:
_sre.SRE_Match object; span=(0, 8), match='\nabc\neee'