11 正则表达式
11.1 简介
1、正则表达式(regular expression)是一个特殊的字符序列,描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串。
2、将匹配的子串替换或者从某个串中取出符合某个条件的子串,或者在指定的文章中抓取特定的字符串等。
11.2 正则表达式模式
1、正则表达式的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,就是匹配不成功。
2、模式字符串使用特殊的语法来表示一个正则表达式:
(1)字母和数字匹配它们自身
(2)多数字母和数字前加一个反斜杠()时会有特殊的含义(\w \d \s…)
(3)特殊的标点符号,只有被转义以后才能匹配自身(. ^ $…)
(3)反斜杠本身需要反斜杠来转义(\)
最好使用原始字符串来表示,如r"\d"
11.3 特殊表达式含义
11.3.1 一般字符
11.3.1.1 . (点)
匹配除换行符以外的任意一个字符。
>>> re.match(r"a.b","acb") #匹配任意一字符
<re.Match object; span=(0, 3), match='acb'>
>>> re.match(r"a.b","a b") #匹配一个空格
<re.Match object; span=(0, 3), match='a b'>
>>> re.match(r"a.b","a中b") #匹配一个中文
<re.Match object; span=(0, 3), match='a中b'>
>>> re.match(r"a.b","a\nb") #不能匹配换行符
>>> re.match(r"a\.b","a.b") #只想匹配“.”
<re.Match object; span=(0, 3), match='a.b'>
>>> re.match(r"a\.b","axb") #None
>>> re.match(r"a.b","a\rb") #匹配\r
<re.Match object; span=(0, 3), match='a\rb'>
>>> re.match(r"a.b","a\tb") #匹配\t
<re.Match object; span=(0, 3), match='a\tb'>
11.3.1.2 \ (反斜杠)
转义一个特殊的字符,使这个字符表示原来字面上的意思。比如:“$”,表示原字符“$”,而不是匹配行尾的意思。
>>> re.match(r"a\.b","a.b") #将表示匹配任意一个字符的“.”转义为字符原来的意思
<re.Match object; span=(0, 3), match='a.b'>
#匹配一个斜杠,需要写两个斜杠,一个斜杠会被当成转义
>>> re.match(r"\\","\\3")
<re.Match object; span=(0, 1), match='\\'>
>>> re.match("\\\\","\\")
<re.Match object; span=(0, 1), match='\\'>
>>> print("\")
File "<stdin>", line 1
print("\")
^
SyntaxError: EOL while scanning string literal
>>> print("\\")
\
总结:
1 正则前面必须加r,这样\d \s\w\b才会有特殊正则含义
例如:print(re.match(r"a\b",“a”))中的\b当做边界去匹配
匹配的结果是a
2 如果不加r,\d \s\w\b全部会被当做+字母去匹配,失去了正则模糊匹配的功能
例如:print(re.match(“a\b”,“a”))中的\b当做+b去匹配的,没有匹配边界的能力
匹配的结果是a\b
11.3.2 预定义字符集
11.3.2.1 […] (方括号)
匹配方括号中出现的任意单个字符
>>> re.match(r"[xyz]","x") #匹配任意一个字符
<re.Match object; span=(0, 1), match='x'>
>>> re.match(r"[xyz]","xy")
<re.Match object; span=(0, 1), match='x'>
>>> re.match(r"[xyz]","y")
<re.Match object; span=(0, 1), match='y'>
>>> re.match(r"[xyz]","z")
<re.Match object; span=(0, 1), match='z'>
11.3.2.2 [^…]
不匹配方括号中列出的单个字符(注意只能针对单个字符)
>>> re.search(r"[^abc]","axx")
<re.Match object; span=(1, 2), match='x'>
>>> re.search(r"[^abc]","bxx")
<re.Match object; span=(1, 2), match='x'>
>>> re.search(r"[^abc]","cxx")
<re.Match object; span=(1, 2), match='x'>
>>> re.search(r"[^abc]","dxx")
<re.Match object; span=(0, 1), match='d'>
>>> re.search(r"[^abc]","abxx")
<re.Match object; span=(2, 3), match='x'>
>>> re.search(r"[^abc]","abc")
11.3.2.3 \d \D
\d:匹配任意一个数字,[0-9]
\D:匹配任意一个非数字字符,等价于[^\d]
>>> re.match(r"a\db\d","a1b2") #\d匹配一个数字
<re.Match object; span=(0, 4), match='a1b2'>
>>> re.match(r"\d+","0123456789") #\d+匹配多个数字
<re.Match object; span=(0, 10), match='0123456789'>
>>> re.match(r"[^\d]","a")
<re.Match object; span=(0, 1), match='a'>
>>> re.match(r"\D","abc")
<re.Match object; span=(0, 1), match='a'>
>>> re.match(r"\D+","abc")
<re.Match object; span=(0, 3), match='abc'>
11.3.2.4 \w \W
\w:匹配任意一个字母、数字或下划线(_),[a-zA-z0-9_]
\W:等价于[^\w]
>>> re.match(r"\w","ac")
<re.Match object; span=(0, 1), match='a'>
>>> re.match(r"\w+","ac")
<re.Match object; span=(0, 2), match='ac'>
>>> re.match(r"\w+","ac_bdc")
<re.Match object; span=(0, 6), match='ac_bdc'>
>>> re.match(r"\W+","!@#$%^&*")
<re.Match object; span=(0, 8), match='!@#$%^&*'>
>>> re.match(r"[^\w]+","!@#$%^&*")
<re.Match object; span=(0, 8), match='!@#$%^&*'>
11.3.2.5 \s \S
\s:匹配任意一个空白字符,[<空格>\t\r\n\v\f]
\S:匹配任意一个非空白字符,等价于[^\s]
>>> re.match(r"\s"," ")
<re.Match object; span=(0, 1), match=' '>
>>> re.match(r"\s+","\n\t\r ")
<re.Match object; span=(0, 5), match='\n\t\r '>
>>> re.match(r"\S+","123WEijk\n\r\t&^%")
<re.Match object; span=(0, 8), match='123WEijk'>
>>> re.findall(r"\S+","123WEijk\n\r\t&^%")
['123WEijk', '&^%']
11.3.3 数量字符集
11.3.3.1 * (星号)
匹配前一个字符0次或多次
>>> re.match(r"\d*","abcd") #任意一个数字匹配0次
<re.Match object; span=(0, 0), match=''>
>>> re.match(r"\d*","12345") #任意一个数字匹配多次
<re.Match object; span=(0, 5), match='12345'>
>>> re.findall(r'\w*','&&a asqwee4')
['', '', 'a', '', '', 'asqwee4', '']
11.3.3.2 + (加号)
匹配前一个字符1次或多次
>>> re.match(r"\d+","12345")
<re.Match object; span=(0, 5), match='12345'>
11.3.3.3 ? (问号)
匹配前一个字符0次或1次
>>> re.match(r"a?","abc")
<re.Match object; span=(0, 1), match='a'>
>>> re.match(r"a?","bc")
<re.Match object; span=(0, 0), match=''>
>>> re.match(r"\d*","12345")
<re.Match object; span=(0, 5), match='12345'>
>>> re.match(r"\d*?","12345")
<re.Match object; span=(0, 0), match=''>
>>> re.match(r"\d+","12345")
<re.Match object; span=(0, 5), match='12345'>
>>> re.match(r"\d+?","12345")
<re.Match object; span=(0, 1), match='1'>
11.3.3.4 {m}
匹配前一个字符m次
re.match(r"a{3}","aaa") #a匹配3次
<re.Match object; span=(0, 3), match='aaa'>
>>> re.match(r"a{3}","aa") #a匹配3次,但字符串只有2个a,所以匹配失败
11.3.3.5 {m,n}
匹配前一个字符m到n次
>>> re.match(r"a{1,3}","a") #匹配1次or2次or3次a
<re.Match object; span=(0, 1), match='a'>
>>> re.match(r"a{1,3}","aa") #匹配1次or2次or3次a
<re.Match object; span=(0, 2), match='aa'>
>>> re.match(r"a{1,3}","aaa") #匹配1次or2次or3次a
<re.Match object; span=(0, 3), match='aaa'>
>>> re.match(r"a{1,3}","aaaa") #匹配1次or2次or3次a,但字符串有4个a,最多匹配3个
<re.Match object; span=(0, 3), match='aaa'>
>>> re.match(r"a{1,3}","b") #匹配1次or2次or3次a,但字符串没有a,匹配失败
11.3.3.6 {m,}
匹配前一个字符至少m次
>>> re.match(r"a{2,}","a") #a至少2次,但字符串只有1个a,匹配失败
>>> re.match(r"a{2,}","aaa") #a至少2次,但字符串有3个a,匹配3个a
<re.Match object; span=(0, 3), match='aaa'>
>>> re.match(r"a{2,}","aa")
<re.Match object; span=(0, 2), match='aa'>
11.3.3.7 {,n}
匹配前一个字符至多n次
>>> re.match(r"a{,2}","aa")
<re.Match object; span=(0, 2), match='aa'>
>>> re.match(r"a{,2}","aaaaa") #a至多2次,但字符串有多个a,所以只匹配2个a
<re.Match object; span=(0, 2), match='aa'>
>>> re.match(r"a{,2}","") #a至多2次,但字符串为空,可以匹配0次,所以返回空
<re.Match object; span=(0, 0), match=''>
11.3.4 边界匹配符
11.3.4.1 ^ $
^:匹配字符串开头,如果是多行则匹配每一行的开头
$:匹配字符串结尾,如果是多行则匹配每一行的结尾
>>> re.match(r"^abc","abc dec") #匹配以abc开头的
<re.Match object; span=(0, 3), match='abc'>
>>> re.findall(r"^a","abc\nadf\navf",re.M) #匹配每一行的开头
['a', 'a', 'a']
>>> re.search(r"abc$","dec ##%abc") #匹配以abc结尾的
<re.Match object; span=(7, 10), match='abc'>
>>> re.search(r"abc$","dec ##%$abc")
<re.Match object; span=(8, 11), match='abc'>
>>> re.findall(r"d$","abcd\nadfd\navfd",re.M) #匹配每一行的结尾
['d', 'd', 'd']
>>> re.search(r"^abc$","abc") #匹配以abc开头和结尾的,只能是abc
<re.Match object; span=(0, 3), match='abc'>
>>> re.search(r"^abc$"," abc ")
11.3.4.2 \A \Z
\A:仅匹配字符串的开始
\Z:匹配字符串结束,如果存在换行,只匹配到换行前的结束字符
>>> re.search(r"\Aabc\Z"," abc ")
>>> re.search(r"\Aabc\Z","abc")
<re.Match object; span=(0, 3), match='abc'>
11.3.4.3 \b \B
\b:情况1:目标字符串的最前面一个字符前一个位置就是边界
最后面的一个字符的后一个位置就是边界
“xyz”:x的前一个位置就是边界,z的后一个位置也是边界
情况2:非字母或数字,称之为边界
" abc xyz mn ",x的前一个位置就是边界,z的后一个位置也是边界
“!abc!xyz!mn!”,所有的叹号都是边界
\B:匹配非单词边界,也就是指单词和空格间的位置
>>> re.match(r"\bxyz\b","xyz")
<_sre.SRE_Match object; span=(0, 3), match='xyz'>
>>> re.match(r" \bxyz\b"," xyz")
<_sre.SRE_Match object; span=(0, 4), match=' xyz'>
>>> re.match(r" \bxyz\b"," xyz ")
<_sre.SRE_Match object; span=(0, 4), match=' xyz'>
>>> s="I am a good boy,sure!"
>>> re.findall(r"\b\w+\b",s)
['I', 'am', 'a', 'good', 'boy', 'sure']
>>> re.findall(r"\ba\b",s)
['a']
>>> re.search(r"\Babc\B","1abc2")
<re.Match object; span=(1, 4), match='abc'>
>>> re.search(r"\babc\b","1abc2")
>>> re.search(r"st\b","a test of")
<re.Match object; span=(4, 6), match='st'>
>>> re.search(r"st\b","tester")
>>> re.search(r"st\B","tester")
<re.Match object; span=(2, 4), match='st'>
>>> re.search(r"st\B","a test of")
>>> re.search(r"\B\w+\B","%hjhhjyu%") #%属于边界,不匹配;h,u是非边界
<re.Match object; span=(2, 7), match='jhhjy'>
11.3.5 逻辑匹配符
11.3.5.1 | (或)
左右正则表达式任意匹配一个,一般放在圆括号中。
>>> re.match(r"(ab|cd)ef","cdef")
<re.Match object; span=(0, 4), match='cdef'>
>>> re.match(r"(ab|cd)ef","abef")
<re.Match object; span=(0, 4), match='abef'>
>>> re.match(r"ab|cdef","abef")
<re.Match object; span=(0, 2), match='ab'>
>>> re.match(r"ab|cdef","cdef")
<re.Match object; span=(0, 4), match='cdef'>
11.3.6 分组匹配符
11.3.6.1 (…)
用括号括起来的正则表达式将被作为有一个分组,从左边依次算起,有多少个左括号就有多少个分组。
>>> re.search(r"(\d+)(abc)","234abc 910j").group(1)
'234'
>>> re.search(r"(\d+)(abc)","234abc 910j").group(2)
'abc'
>>> re.search(r"(\d+)(abc)","234abc 910j").groups()
('234', 'abc')
>>> re.findall(r"(\d+)(abc)","234abc 910j")
[('234', 'abc')]
>>> re.match(r"(ab|cd)x","abx")
<re.Match object; span=(0, 3), match='abx'>
>>> re.match(r"(ab|cd)x","abx").group(1) #只取分组结果
'ab'
>>> re.search(r"(ab)(ab)","abab").group(2)
'ab'
>>> re.search(r"(ab)(ab)","abab").group(1)
'ab'
>>> re.search(r"(ab)(ab)","abab").group()
'abab'
>>> re.search(r"(ab)(ab)","abab").groups()
('ab', 'ab')
>>> re.search(r"(ab){2}","abab")
<re.Match object; span=(0, 4), match='abab'>
>>> re.search(r"(ab){2}","abab").groups()
('ab',)
>>> res=re.findall(r"((\d+)([a-z]+)+)","123abc890xde")
>>> res
[('123abc', '123', 'abc'), ('890xde', '890', 'xde')]
>>> res[0]
('123abc', '123', 'abc')
>>> res[1]
('890xde', '890', 'xde')
>>> res[1][0]
'890xde'
>>> re.search(r"((\d+)(\w+))+","123abc890xde")
<re.Match object; span=(0, 12), match='123abc890xde'>
>>> re.search(r"((\d+)(\w+))+","123abc890xde").groups()
('123abc890xde', '123', 'abc890xde')
11.4 编译正则表达式
将正则表达式的字符串形式编译为pattern实例,然后使用pattern实例处理文本并获取匹配结果(一个Match实例),最后使用Match实例获取信息,进行其他操作。
>>> import re
>>> pattern=re.compile(r"hello")
>>> pattern.match("hello world").group()
'hello'
>>> pattern.search("python:hello world").group()
'hello'
>>> pattern.findall("python:hello world")
['hello']
11.4.1 re.compile
re.compile(pattern,flags=0) re.compile(pattern,re.I|re.M)
这个方法是pattern类的工厂方法,目的是将正则表达式pattern编译成pattern对象,并返回该对象。
使用该pattern模式唯一的好处就是,一处编译,多处复用。
>>> ro=re.compile(r"\d+")
>>> ro.match("123456")
<re.Match object; span=(0, 6), match='123456'>
>>> patt=re.compile(r"[a-z]+",re.I)
>>> patt.match("aBcD")
<re.Match object; span=(0, 4), match='aBcD'>
11.4.2 pattern对象属性及方法
(1)pattern对象是一个编译好的正则表达式,也就是通过re.compile()函数编译后得到的结果。
(2)通过pattern提供的一系列方法可以对文本进行匹配查找。
(3)pattern不能直接实例化,必须使用re.complit()函数进行构造。
(4)pattern提供了几个可读属性及方法用于处理正则表达式。
>>> patt=re.compile("[a-zA-Z]")
>>> dir(patt)
['__class__', '__copy__', '__deepcopy__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'findall', 'finditer', 'flags', 'fullmatch', 'groupindex', 'groups', 'match', 'pattern', 'scanner', 'search', 'split', 'sub', 'subn']
11.4.2.1 flags属性
该属性表示获取编译时用的匹配模式,数字形式。
>>> ro=re.compile(r"abc",re.I)
>>> ro.match("abc")
<re.Match object; span=(0, 3), match='abc'>
>>> ro.match("Abc")
<re.Match object; span=(0, 3), match='Abc'>
>>> ro.flags
34
11.4.2.2 groups属性
该属性表示获取表达式中分组的数量。
>>> patt=re.compile("(\w\w)-(\d\d)")
>>> patt.groups
2
11.5 pattern.match
在字符串的pos位置开始尝试匹配,pattern是编译后返回的对象。
pattern.match(string,pos,endpos) pos默认0,endpos默认len(string)
>>> ro=re.compile(r"abc",re.I)
>>> ro.match("123as")
>>> ro.match("abc123")
<re.Match object; span=(0, 3), match='abc'>
>>> ro.match("123abc",3) #加上开始位置
<re.Match object; span=(3, 6), match='abc'>
>>> ro.match("123abc123",3,6) #加上结束位置
<re.Match object; span=(3, 6), match='abc'>
>>> ro.match("123abc123",3,4) #未匹配结束就到了结束位置,匹配失败,返回None
11.6 re.match
从字符串的开头进行匹配
re.match(pattern,string,flags) re.match(r"abc",“aBc”,re.I)
>>> re.match(r"abc","abcdefg")
<re.Match object; span=(0, 3), match='abc'>
>>> re.match(r"abc","1abcdefg")
>>> re.match(r"abc","abcdefg").group()
'abc'
>>> re.match(r"abc","1abcdefg").group()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
匹配成功,返回一个匹配成功后的Match对象
>>> match_object=re.match(r"abc","abcdefg")
>>> type(match_object)
<class 're.Match'>
>>> dir(match_object)
['__class__', '__copy__', '__deepcopy__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'end', 'endpos', 'expand', 'group', 'groupdict', 'groups', 'lastgroup', 'lastindex', 'pos', 're', 'regs', 'span', 'start', 'string']
>>> match_object.span()
(0, 3)
>>> match_object.strring()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 're.Match' object has no attribute 'strring'
>>> match_object.string
'abcdefg'
没有匹配到,返回None
>>> match_object=re.match(r"abc","1abcdefg")
>>> match_object
>>> print(match_object)
None
>>> if match_object:
... print("匹配到了")
... else:
... print("没匹配到")
...
没匹配到
11.7 pattern.search
在字符串的[pos,endpos]区间匹配,pattern是编译后返回的对象。
pattern.search(string,pos,endpos) pos默认0,endpos默认len(string)
>>> ro=re.compile(r"abc",re.I)
>>> ro.search("123abc")
<re.Match object; span=(3, 6), match='abc'>
>>> ro.search("123abc",3)
<re.Match object; span=(3, 6), match='abc'>
>>> ro.search("123abc",3,6)
<re.Match object; span=(3, 6), match='abc'>
>>> ro.search("123abc",3,4)
11.8 re.search
扫描整个字符串并返回第一次成功的匹配对象,如果匹配失败,返回None.
re.search(pattern,string,flags) re.search(r"abc",“1213aBcd”,re.I)
>>> re.search(r"\bxyz\b","#xyz#")
<re.Match object; span=(1, 4), match='xyz'>
>>> re.search(r"abc","123 abc 456")
<re.Match object; span=(4, 7), match='abc'>
>>> re.search(r"\babc\b","xyabcmn")
>>> re.search(r"\babc\b","xy abc mn")
<_sre.SRE_Match object; span=(3, 6), match='abc'>
>>> re.search(r"\babc\b","xy!abc!mn")
<_sre.SRE_Match object; span=(3, 6), match='abc'>
>>> re.search(r"\babc\b","xy!abc")
<_sre.SRE_Match object; span=(3, 6), match='abc'>
>>> re.search(r"\babc\b","abc xx")
<_sre.SRE_Match object; span=(0, 3), match='abc'>
11.9 re.findall
查找所有能匹配的字符串,存于列表中,返回该列表。
正则表达式分组情况会体现在返回的列表中。
re.findall(pattern,string,flags) re.findall(r"abc",“1213aBcd”,re.I)
>>> re.findall(r"[a-zA-Z]+","I am a girl!")
['I', 'am', 'a', 'girl']
>>> re.findall(r"\d+","1234-5678-9876")
['1234', '5678', '9876']
分组情况,只返回括号中匹配的内容而不是整个正则表达式匹配的内容
(1)元组中元素个数与分组个数(括号对数)相同
(2)字符串顺序与括号顺序一致
(3)字符串内容与括号中正则表达式相对应
>>> re.findall(r"\d+-","1234-5678-9876-")
['1234-', '5678-', '9876-']
>>> re.findall(r"(\d+)-","1234-5678-9876-")
['1234', '5678', '9876']
>>> re.findall(r"(\d+)(-)","1234-5678-9876-")
[('1234', '-'), ('5678', '-'), ('9876', '-')]
>>> re.findall(r"((\d+)(-))","1234-5678-9876-")
[('1234-', '1234', '-'), ('5678-', '5678', '-'), ('9876-', '9876', '-')]
小练习:取www.net.com.edu和www.baidu.com
import re
add = 'https://www.net.com.edu//action=?asdfsd and other https://www.baidu.com//a=b'
print(re.findall(r"(w{3}\.(\w+\.)+(com|edu))",add))
print(re.findall(r"((w{3}\.)(\w+\.)+(com|edu))",add))
print(re.findall(r"http[s]?://(.+?)//",add))
11.10 pattern.findall
在字符串的[pos,endpos]区间查找所有匹配的子串,pattern是编译后返回的对象。
pattern.findall(string,pos,endpos) pos默认0,endpos默认len(string)
>>> patt=re.compile(r"(\d+)(-)")
>>> patt.findall("1234-5678-4567-",5)
[('5678', '-'), ('4567', '-')]
>>> patt.findall("1234-5678-4567-",5,10)
[('5678', '-')]
11.11 finditer
与findall一样,匹配所有满足条件的子串,但是返回的是一个迭代器,而不是存有所有结果的列表。
re.finditer(pattern,string,flags) re.finditer(r"abc",“1213aBcd”,re.I)
pattern.finditer(string,pos,endpos) pos默认0,endpos默认len(string)
>>> res=re.finditer(r"\d+","123abc456bbf789ddd")
>>> res
<callable_iterator object at 0x00000173B68E2108>
>>> for i in res:
... print(i.group())
...
123
456
789
>>> res=re.findall(r"\d+","123abc456bbf789ddd")
>>> res
['123', '456', '789']
>>> patt=re.compile(r"(\d+)(-)")
>>> patt.finditer("1234-5678-9068-")
<callable_iterator object at 0x000001F730D28208>
>>> res=patt.finditer("1234-5678-9068-")
>>> for i in res:
... print(i.group())
...
1234-
5678-
9068-
>>> res=patt.finditer("1234-5678-9068-",5)
>>> for i in res:
... print(i.group())
...
5678-
9068-
11.12 pattern.split
将字符串用给定的正则表达式匹配的字符串进行分割,并返回分割后的结果list.
pattern.split(string,maxsplit) maxsplit默认为0,表示全部分割
>>> patt=re.compile(r"[a-z]",re.I)
>>> patt.split("1a2B3c4D")
['1', '2', '3', '4', '']
>>> patt.split("1a2B3c4D",2)
['1', '2', '3c4D']
11.13 re.split
re.split(pattern,string,maxsplit,flags) re.split(r"[a-z]","1a2B3c4D",0,re.I)
>>> re.split(r"[a-z]","1a2B3c4D",0,re.I)
['1', '2', '3', '4', '']
>>> re.split(r"[a-z]","1a2B3c4D",2,re.I)
['1', '2', '3c4D']
11.14 pattern.sub
pattern.sub(repl,string,count) repl替换string中匹配的子串 count默认为0
(1)repl为字符串
>>> s="i say, helllo word"
>>> re.findall(r"(\w+) (\w+)",s)
[('i', 'say'), ('helllo', 'word')]
>>> p=re.compile(r"(\w+) (\w+)")
>>> p.sub(r"\2 \1",s) #\2, \1表示分组引用,分别代表第二个分组,第一个分组
'say i, word helllo'
>>> p=re.compile(r"(?P<g1>\w+) (?P<g2>\w+)") #第一个分组名为g1,第二个分组名为g2
>>> p.sub(r"\g<g2>,\g<g1>",s)
'say,i,world,hello'
(2)repl为函数
pattern.sub(func,string,count)
必须传一个Match对象,并且必须返回一个字符串用于替换(返回的字符串中不能再引用分组)。
import re
p = re.compile(r'(\w+) (\w+)') #p是这个正则,匹配到内容后,替换func返回的结果,这个操作会做多次
s = 'i say, hello world!'
def func(m): #m是p的匹配对象(Match对象)
print("group1:",m.group(1))
print("group2:",m.group(2))
return m.group(2).title() +" "+ m.group(1).title()
print(p.sub(func, s))
#执行结果:
group1: i
group2: say
group1: hello
group2: world
Say I, World Hello!
11.15 re.sub
re.sub(pattern,repl,string,count=0,flags=0)
(1)repl为字符串
>>> re.sub(r"\d+","**","1a2b3c")
'**a**b**c'
>>> re.sub(r"\d+","**","a2b3c4")
'a**b**c**'
>>> re.sub(r"\d+","**","a2b3c4",2) #替换次数
'a**b**c4'
(2)repl为函数
import re
def multiply(m):
# 将分组0的值转成整型
v = int(m.group(0))
print(v)
return str(v * 2)
# 使用multiply方法作为第二个参数,将匹配到的每个数字作为参数传入multiply函数,处理后将返回结果替换为相应字符串
result = re.sub("\d+", multiply, "10 20 30 40 50")
print(result)
#执行结果
10
20
30
40
50
20 40 60 80 100
>>> s="1 2 3 4 5"
>>> def func(m):
... print(m.group())
... return str(int(m.group())+1)
...
>>> re.sub(r"\d",func,s)
1
2
3
4
5
'2 3 4 5 6'
11.16 pattern.subn
与sub用法一样,只是结果返回一个元组,第一个元素是替换后的新字符串,第二个元素是替换的次数。
11.17 re.subn
与sub用法一样,只是结果返回一个元组,第一个元素是替换后的新字符串,第二个元素是替换的次数
>>> s="^&%today&(is%%fine# day!"
>>> re.subn(r"\W+"," ",s)
(' today is fine day ', 5)
>>> re.subn(r"\d+","*","a1b23c456")
('a*b*c*', 3)
>>> s="1 2 3 4 5"
>>> def func(m):
... print(m.group())
... return str(int(m.group())+1)
...
>>> re.subn(r"\d",func,s)
1
2
3
4
5
('2 3 4 5 6', 5)
11.18 Match对象的属性和方法
11.18.1 string
获取匹配时使用的字符串对象。
>>> res=re.match(r"\d+","12345abcd")
>>> res
<re.Match object; span=(0, 5), match='12345'>
>>> res.string
'12345abcd'
11.18.2 re
获取匹配时使用的pattern对象,也就是正则表达式对象。
>>> res=re.match(r"\d+","12345abcd")
>>> res
<re.Match object; span=(0, 5), match='12345'>
>>> res.re
re.compile('\\d+')
11.18.3 span()
span([group]) 以元组形式返回某个元组的匹配字符串的开始和结束索引位置
>>> re.search("\d+","abc123").span()
(3, 6)
>>> re.search("\d+","abc(1)23").span()
(4, 5)
>>> m = re.search(r'(\w+)! (\w+) (\w+)','HMan! gloryroad train')
>>> print(m.span(1))
(0, 4)
>>> print(m.span(2))
(6, 15)
>>> print(m.span(3))
(16, 21)
>>> print(m.span())
(0, 21)
>>> re.search("abc(\d+)","abc123").span()
(0, 6)
>>> re.search("abc(\d+)","abc123").span(1)
(3, 6)
11.18.4 expand()
expand(template) 将匹配到的分组代入template中然后返回
import re
m = re.search(r'(\w+)! (\w+) (\w+)','HMan! gloryroad train')
#使用\id引用分组
print(m.expand(r"'\\id' result:\3 \2 \1"))
#使用\g<id>引用分组
print(m.expand(r"'\\g<id>' result:\g<3> \g<2> \g<1>"))
m = re.search(r'(?P<g1>\w+)! (?P<g2>\w+) (?P<g3>\w+)','HMan! gloryroad train')
#使用\g<name>引用分组
print(m.expand(r"'\\g<name>' result:\g<g3> \g<g2> \g<g1>"))
#执行结果
'\id' result:train gloryroad HMan
'\g<id>' result:train gloryroad HMan
'\g<name>' result:train gloryroad HMan
11.19 修饰符
re.I|re.M 表示忽略大小写和多行匹配同时生效
11.19.1 Re.I (忽略大小写匹配)
>>> ro=re.compile(r"abc",re.I)
>>> ro.match("abc")
<re.Match object; span=(0, 3), match='abc'>
>>> ro.match("Abc")
<re.Match object; span=(0, 3), match='Abc'>
11.19.2 re.M (多行匹配)
import re
s="abc123\ncde456\nghi789\n"
print(re.findall(r"^[a-zA-Z]+",s,re.M)) #['abc', 'cde', 'ghi']
print(re.findall(r"[0-9]+$",s,re.M)) #['123', '456', '789']
import re
p = re.compile(r'^.*[a-z]+$', re.I|re.M)
string = "I am a Boy\nYour a beautiful Girl\nRight"
matchResult = p.findall(string)
if matchResult:
print(matchResult)
else:
print("no string found!")
['I am a Boy', 'Your a beautiful Girl', 'Right']
11.19.3 Re.S (.可以匹配换行符)
>>> re.match(r"a.b","a\nb")
>>> re.match(r"a.b","a\nb",re.S)
<re.Match object; span=(0, 3), match='a\nb'>
>>> re.match(r"a.b","a\nb",re.DOTALL)
<re.Match object; span=(0, 3), match='a\nb'>
11.19.4 Re.X (正则表达式内写注释)
import re
a = re.compile(r'''\d+ # 匹配至少1个连续的数字,自定义注释
\. # 匹配点(.)
\d* # 匹配数字至少0个''', re.X)
b = re.compile(r"\d+\.\d*") #a和b的正则表达式等价的
print(a.search("test12.58 2.0 abc 3.8").group())
Json串例子:匹配json串中的大括号内容
import re
import requests
str = """
var localCity = [{
provinceCode: "110000",
cityCode: "110100",
text: "北京",
dpt: '20127',
carNo: '京A',
pName: '北京市',
cName: '北京市'
},
{
provinceCode: "310000",
cityCode: "310100",
text: "上海",
dpt: '2022003',
carNo: '沪A',
pName: '上海市',
cName: '上海市'
}]
"""
cont = re.findall(r"{.*?}", str,re.M|re.S)
print("result:",cont)
print("result:",cont[0]) result: ['{\n provinceCode: "110000",\n cityCode: "110100",\n text: "北京",\n dpt: \'20127\',\n carNo: \'京A\',\n pName: \'北京市\',\n cName: \'北京市\'\n}', '{\n provinceCode: "310000",\n cityCode: "310100",\n text: "上海",\n dpt: \'2022003\',\n carNo: \'沪A\',\n pName: \'上海市\',\n cName: \'上海市\'\n}']
result: {
provinceCode: "110000",
cityCode: "110100",
text: "北京",
dpt: '20127',
carNo: '京A',
pName: '北京市',
cName: '北京市'
}
11.20 有名分组
(?P正则表达式)
import re
s = "ip='230.192.168.78',version='1.0.0'"
res = re.search(r"ip='(?P<ip>\d+\.\d+\.\d+\.\d+).*", s)
print(res.group('ip'))#通过命名分组引用分组
230.192.168.78
import re
matchResult =re.search(r'(?P<last>\w+),(?P<first>\w+),(?P<phone>\S+)',"phone,number,15817934235")
#使用命名分组获取第一个分组的内容
print(matchResult.group('last'))
#使用命名分组获取第二个分组的内容
print(matchResult.group('first'))
#使用命名分组获取第三个分组的内容
print(matchResult.group('phone'))
phone
number
15817934235
11.21 后向引用
当用“()”定义了一个正则表达式分组后,正则引擎就会把匹配的组按照顺序进行编号,然后存入缓存中。这样就可以在后面对已经匹配过的内容进行引用,这就叫后向引用。
(1)通过\id引用
>>> re.search(r"(\d+)abc\1","123abc123") #\1表示引用第一个分组
#\1表示abc后面匹配的必须跟abc前面匹配的内容一致,与r"(\d+)abc(\d+)"不一样
<re.Match object; span=(0, 9), match='123abc123'>
>>> re.search(r"(\d+)abc\1","123abc323")
<re.Match object; span=(2, 7), match='3abc3'>
>>> re.search(r"(\d+)(\w)abc\2","124xabcx") #\2表示引用第二个分组
<re.Match object; span=(0, 8), match='124xabcx'>
>>> re.search(r"(\d+)(\w)abc\2","124xabcy") #abc前后不一致,匹配失败
(2)通过(?P=name)引用
>>> re.search(r"(?P<digit>\d+)abc(?P=digit)","123abc123")
<re.Match object; span=(0, 9), match='123abc123'>