通用字符匹配
语法 | 通配符匹配作用解析 |
. | 匹配除换行符之外的任意一个字符 |
* | 匹配前一个字符出现零次或任意多次 |
+ | 匹配前一个字符出现1次或任意多次 |
? | 匹配前一个字符出现1次或0次 |
^ | 匹配以指定字符开头的数据 |
$ | 匹配以指定字符结尾的数据 |
{m} | 匹配前一个字符出现过m次的记录 |
{n,m} | 匹配前一个字符,最少出现n次,最多出现m次 |
匹配任意一个字符(.) 默认匹配除\n之外的任意一个字符,若指定flag=DOTALL则匹配包括换行在内的字符.
>>> re.search("hel.o","hello lyshark,hello world").group()
'hello'
>>> re.findall("hel.o","hello lyshark hello world")
['hello', 'hello']
匹配前一个字符出现0至任意多次(*) 匹配星号前面的字符出现0次,或任意多次.
>>> re.findall("ab*","abccba23acbcabb")
['ab', 'a', 'a', 'abb']
匹配前一个字符出现1次或任意多次(+) 匹配加号前面的字符出现过1次,或任意多次,至少出现一次.
>>> re.findall("ab+","abccba23acbcabb")
['ab', 'abb']
匹配前一个字符出现1次或0次(?) 匹配前一个字符出现过1次或0次,允许出现0次.
>>> re.findall("ab?","ab,abc,abb,abcd,a,acd,abc")
['ab', 'ab', 'ab', 'ab', 'a', 'a', 'ab']
>>> re.findall("ab?","ab,a,abc,abcde")
['ab', 'a', 'ab', 'ab']
匹配开头与结尾(^): ^匹配指定字符开头的数据,匹配指定字符结尾的数字.
>>> re.search(r"^h","hello world").group()
'h'
>>> re.search(r"world$","hello\nworld").group()
'world'
>>> re.search(r"^a","\nabc\ndef",flags=re.MULTILINE).group()
'a'
>>> re.search("foo$","bfoo\nsdfsf",flags=re.MULTILINE).group()
'foo'
匹配前一个字符出现次数(x{m}) 匹配前一个字符x,出现过m次的行.
>>> re.search("hello{2}","hello,helloo,hellooo,helloooo").group()
'helloo'
>>> re.search("hello{3}","hello,helloo,hellooo,helloooo").group()
'hellooo'
匹配前一个字符出现次数(x{n,m}) 匹配前一个字符x,最少出现过n次,最多出现过m次.
>>> re.search("hello{1,2}","hello,helloo,hellooo,helloooo").group()
'hello'
>>> re.findall("hello{1,2}","hello,helloo,hellooo,helloooo")
['hello', 'helloo', 'helloo', 'helloo']
脱意字符的匹配(\) 转义字符,通常情况下使后一个字符改变原来的意思,也叫做脱意字符.
>>> re.search("..\\t","hello\t lyshark\n").group()
'lo\t'
>>> re.search("\\t","hello\t lyshark\n").group()
'\t'
>>> re.search("\t","hello\t lyshark\n").group()
'\t'
>>> re.search(r"\\","hello\\lyshark").group()
'\\'
匹配查找范围([]) 匹配查找指定的数据范围,通常使用[0-9] [a-z] [A-Z]
这几个匹配格式.
>>> re.search("[0-9]","hello 1,2,3,4,5").group() # 匹配第一次出现数字的行
'1'
>>> re.search("[0-9]","hello a12 b23 34a 45t").group()
'1'
>>> re.findall("[0-9]","hello 1,2,3,4,5") # 匹配所有出现数字的行
['1', '2', '3', '4', '5']
>>> re.findall("[0-9]","hello b23 34a 45t wan")
['2', '3', '3', '4', '4', '5']
>>> re.search("[^0-9]","hello 1,2,3,4,5").group() # 匹配开头不是0-9的单个字符
'h'
>>> re.search("[^0-9]*","hello 1,2,3,4,5").group() # 匹配开头不是0-9的单行行
'hello'
>>> re.search(r"[aeiou]","Hello LyShark").group()
'e'
匹配查找空白字符(s) 匹配空白字符
>>> re.search("\s+","ab\tc1\n3").group()
'\t'
>>> re.search("\s+","ab c1\n3").group()
' '
选择性匹配(|) 匹配选择竖线左边,或者右边的任意一种情况.
>>> re.search("abc|ABC","ABCBabcCD").group()
'ABC'
>>> re.findall("abc|ABC","ABCBabcCD")
['ABC', 'abc']
实现分组匹配((?P...)) 匹配条件并自动分组,其中?P<..>是固定写法,后面紧跟正则规则.
>>> number = "371481199306143242"
>>> re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})",number).groupdict()
{'province': '3714', 'city': '81', 'birthday': '1993'}
>>> re.search("(?P<name>[a-zA-Z]+)(?P<age>[0-9]+)","lyshark22").groupdict("temp")
{'name': 'lyshark', 'age': '22'}
针对IP地址与MAC地址的提取:
>>> re.search("^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$","192.168.1.1")
<re.Match object; span=(0, 11), match='192.168.1.1'> # 匹配IP地址
>>> re.match(r"^\s*\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\s*$","192.168.1.100")
<re.Match object; span=(0, 13), match='192.168.1.100'> # 匹配IP地址
>>> string_ip = "is this 236.168.192.1 ip 12321"
>>> result = re.findall(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", string_ip)
>>> result
['236.168.192.1']
>>> string=re.compile(r'((1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.){3}(1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)')
>>> print(string.search('245.255.256.25asdsa10.11.244.10').group())
10.11.244.10
>>> string_IPv6="1050:0:0:0:5:600:300c:326b" # 匹配IPV6地址(大小写不敏感)
>>> re.match(r"^(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}$", string_IPv6, re.I)
<re.Match object; span=(0, 26), match='1050:0:0:0:5:600:300c:326b'>
>>> re.findall(r"(?<![:.\w])(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}(?![:.\w])", string_IPv6, re.I)
['1050:0:0:0:5:600:300c:326b']
>>> re.match(r"^\s*([0-9a-fA-F]{2,2}:){5,5}[0-9a-fA-F]{2,2}\s*$","AB:1F:44:5B:3B:4A")
<re.Match object; span=(0, 17), match='AB:1F:44:5B:3B:4A'> # 匹配一个MAC地址
针对网址与端口的匹配:
>>> re.search(r"^(http|https?:\/\/)([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$","https://www.baidu.com")
<re.Match object; span=(0, 21), match='https://www.baidu.com'> # 匹配网址
>>> re.findall(r"([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{4}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])","hello 443")
['4', '4', '3'] # 匹配端口号
>>> re.search(r'^(http|https?:\/\/)([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?(:([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{4}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]))?$',"http://www.baidu.com:80")
<re.Match object; span=(0, 23), match='http://www.baidu.com:80'>
>>> re.search(r'^(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])(:([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{4}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]))?$',"192.168.1.100:443")
<re.Match object; span=(0, 17), match='192.168.1.100:443'>
针对时间格式的匹配:
>>> re.search(r"(\d{4}-\d{1,2}-\d{1,2})","2019-01-12")
<re.Match object; span=(0, 10), match='2019-01-12'>
>>> re.findall(r"(\d{4}-\d{1,2}-\d{1,2})","2019-01-12,2010-12-11")
['2019-01-12', '2010-12-11']
>>> re.findall(r"\d{4}[-/]\d{2}[-/]\d{2}","2019-01-12,2010/12/11")
['2019-01-12', '2010/12/11']
>>> re.search(r"(\d{1,2}/(Jan|Feb|Mar|Apr|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/\d{4})","2019-01-12,21/Nov/2019").group()
'21/Nov/2019'
>>> re.findall(r"(\d{1,2}:\d{1,2})","2010-12-11 12:11")
['12:11']
>>> re.findall(r"(\d{1,2}:\d{1,2}:\d{1,2})","2010-12-11 12:11:22,09:25:30")
['12:11:22', '09:25:30']
>>> re.search(r"(\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2})","2010-12-11 12:11")
<re.Match object; span=(0, 16), match='2010-12-11 12:11'>
>>> re.findall(r"(\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2})","2010-12-11 12:11")
['2010-12-11 12:11']
匹配邮箱/手机号/身份证:
>>> re.search("^1[3|4|5|8]\d{9}$","18264856987")
<re.Match object; span=(0, 11), match='18264856987'> # 匹配手机号
>>> re.search("[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+","182648@qq.com")
<re.Match object; span=(0, 13), match='182648@qq.com'> # 匹配一个邮箱
>>> re.findall(r'(^[1-8][0-7]{2}\d{3}([12]\d{3})(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}([0-9X])$)',"33070219630306041X")
[('33070219630306041X', '1963', '03', '06', 'X')] # 匹配身份证号
针对密码验证的匹配: 此处的匹配正则常用于用户名密码的过滤.
>>> re.findall("[\u4e00-\u9fa5]","你好")
['你', '好'] # 匹配中文字符
>>> re.findall("^[\u4e00-\u9fa5_a-zA-Z0-9]{4,10}$","1233")
['1233'] # 单纯限制字符的输入长度
>>> re.findall(r"^[a-zA-Z][a-zA-Z0-9_]{4,15}$","password")
['password'] # 允许输入最小5-15个字符的密码,允许使用下划线.
>>> re.findall(r"^[a-zA-Z]\w{5,17}$","passw3")
['passw3'] # 以字母开头,长度在6~18之间,只能包含字母、数字和下划线
>>> re.findall("^(?!_)(?!.*?_$)[a-zA-Z0-9_\u4e00-\u9fa5]+$","1233")
['1233'] # 限制不能以下划线开头和结尾
常用匹配函数
函数与方法名 | 通配符匹配作用解析 |
regex.match | 从字符串开头位置匹配查找,如果0个或多个字符被匹配则返回相应的匹配对象,如果不匹配则返回None. |
regex.search | 扫描整个字符串,查找正则匹配到的字串中第一次出现的位置,并返回相应的匹配对象,如果匹配失败则返回None. |
regex.findall | 搜索字符串中与正则表达式匹配的所有子串,也就是查找字符串中所有的匹配结果,并且以列表的形式返回数据. |
regex.sub | 字符串的替换,简单来说就是替换字符串中与正则表达式匹配的指定数量的子串,最后返回替换修改后的字符串. |
regex.split | 以正则表达式匹配的字符串作为分隔符,对一个字符串进行分割,以列表形式返回分割后的各个字符串. |
match.expand | 通过得到的匹配对象来构造并返回一个新的字符串,未被匹配到的分组将被替换为一个空字符串. |
match.group | 返回一个或多个指定捕获组所匹配到的内容,如果只有1个参数则返回单独的字符串,多参数返回元组. |
match.groups | 返回一个包含所有分组所匹配内容的元组,如果某个分组没有匹配到内容,则取defalult所指定的值. |
match.groupdict | 返回一个包含所有命名分组名称及其所匹配内容的字典对象,如果某个分组没有匹配到内容则取默认值. |
regex.match() 从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回None.
match(pattern,string,flags=0)
# pattern: 正则模型
# string : 要匹配的字符串
# falgs : 匹配模式
#------------------------------------------------
# 未分组情况下.
>>> origin = "hello alex bcd abcd lge acd 19"
>>>
>>> ret = re.match("h\w+",origin)
>>> print(ret.group()) #获取匹配到的所有结果
>>> print(ret.groups()) #获取模型中匹配到的分组结果
>>> print(ret.groupdict()) #获取模型中匹配到的分组结果
# 有分组情况下. 提取匹配成功的指定内容(先匹配成功全部正则,再匹配成功的局部内容提取出来)
>>> ret = re.match("h(\w+).*(?P<name>\d)$",origin)
>>> print(r.group()) #获取匹配到的所有结果
>>> print(r.groups()) #获取模型中匹配到的分组结果
>>> print(r.groupdict()) #获取模型中匹配到的分组中所有执行了key的组
regex.search() 搜索整个字符串去匹配第一个符合条件的数据,未匹配成功返回None.
>>> origin = "hello alex bcd abcd lge acd 19"
>>>
>>> re.search("^h\w+",origin).group() #匹配开头是h的后面是任意字符的
'hello'
>>> re.search("a\w+",origin).group() #匹配a开头后面是任意字符的
'alex'
>>> re.search("(?P<name>a\w+)",origin).groupdict()
{'name': 'alex'} #分组匹配并过滤出alex
>>> re.search("(?P<姓名>[a-zA-Z]+)(?P<年龄>[0-9]+)","lyshark22").groupdict()
{'姓名': 'lyshark', '年龄': '22'} #匹配字符串,并分组打印出结果
regex.findall() 获取非重复的匹配列表,且每一个匹配均是字符串,空的匹配也会包含在结果中.
>>> origin = "hello alex bcd abcd lge acd 19"
>>> re.findall("al\w+",origin)
['alex'] #匹配到单个结果,则以单列表返回
>>> re.findall("a\w+",origin)
['alex', 'abcd', 'acd'] #匹配到多个结果,则以列表形式返回
regex.sub() 先匹配查找结果,然后进行字串的替换,也就是替换匹配成功的指定位置字符串.
sub(pattern,repl,string,count=0,flags=0)
# pattern: 正则模型
# repl : 要替换的字符串或可执行对象
# string : 要匹配的字符串
# count : 指定匹配个数
# flags : 匹配模式
>>> origin = "hello alex bcd abcd lge acd 19"
>>> re.sub("a[a-z]+","999999",origin,1) #匹配以a开头则字串,并替换成9999,替换1次
'hello 999999 bcd abcd lge acd 19'
>>> re.sub("a[a-z]+","999999",origin,2) #匹配以a开头则字串,并替换成9999,替换2次
'hello 999999 bcd 999999 lge acd 19'
regex.split() 字符串切割函数,用来实现对指定字符串的分割工作,根据正则匹配分割字符串.
split(pattern,string,maxsplit=0,flags=0)
# pattern: 正则模型
# string : 要匹配的字符串
# maxsplit:指定分割个数
# flags : 匹配模式
>>> origin = "hello alex bcd abcd lge acd 19"
>>> re.split("alex",origin,1) #无分组切割
['hello ', ' bcd abcd lge acd 19']
>>> re.split("(alex)",origin,1) #有分组,以alex最为分隔符,切割字符串
['hello ', 'alex', ' bcd abcd lge acd 19']
单独匹配
>>> ptr = re.compile(r"[A-Z]")
>>> ptr.search("Hello lyshark")
<re.Match object; span=(0, 1), match='H'>
>>> ptr.findall("Hello lyshark")
['H']
re.DOTALL
# 正则表达式默认以单行开始匹配的
import re
def re_pattern_syntax():
# .表示任意单一字符
# *表示前一个字符出现>=0次
# re.DOTALL就可以匹配换行符\n,默认是以行来匹配的
print(re.match(r'.*', 'abc\nedf').group())
print('*' * 80)
print(re.match(r'.*', 'abc\nedf',re.DOTALL).group())
if __name__ == '__main__':
re_pattern_syntax()
re.MULTILINE
# 正则表达式默认以单行开始匹配的
import re
def re_pattern_syntax1():
# ^表示字符串开头(单行)
# re.MULTILINE多行匹配字符串开头
print(re.findall(r'^abc', 'abc\nedf'))
print('*' * 80)
print(re.findall(r'^abc', 'abc\nabc',re.MULTILINE))
def re_pattern_syntax2():
# $表示字符串结尾
# re.MULTILINE表示行的结束
print(re.findall(r'abc\d$', 'abc1\nabc2'))
print('*' * 80)
print(re.findall(r'abc\d$', 'abc1\nabc2',re.MULTILINE))
if __name__ == '__main__':
re_pattern_syntax1()
re_pattern_syntax2()
?非贪婪模式
import re
def re_pattern_syntax4():
# greedy贪婪/non-greedy非贪婪,默认的是贪婪的匹配
s = '<H1>title</H1>'
print(re.match(r'<.+>', s).group()) #贪婪模式会匹配尽量多的匹配
print(re.match(r'<.+?>', s).group()) #非贪婪模式匹配尽量少的匹配
print(re.match(r'<(.+)>', s).group(1))
print(re.match(r'<(.+?)>', s).group(1))
def re_pattern_syntax5():
# {m}/{m,}/{m,n}
print(re.match(r'ab{2,4}', 'abbbbbbb').group()) #贪婪模式尽量匹配多
print(re.match(r'ab{2,4}?', 'abbbbbbb').group()) #非贪婪模式尽量匹配少
print('*' * 80)
if __name__ == '__main__':
re_pattern_syntax4()
re_pattern_syntax5()
re.I/re.IGNORECASE
import re
def re_pattern_flags():
# re.I/re.IGNORECASE
print(re.match(r'(Name)\s*:\s*(\w+)','NAME : Joey',re.IGNORECASE).groups())
print('*' * 80)
if __name__ == '__main__':
re_pattern_syntax_meta_char()
re.VERBOSE
import re
def re_pattern_flags1():
# re.VERBOSE此标识位可以添加注释/re.compile
s = 'the number is 20.5'
r = re.compile(r'''
\d+ # 整数部分
\.? # 小数点,可能包含也可能不包含
\d* # 小数部分,可选
''',re.VERBOSE)
print(re.search(r,s).group())
print(r.search(s).group())
print('*' * 80)
if __name__ == '__main__':
re_pattern_syntax_meta_char1()