python中的正则表达式

正则表达式中常用字符含义

  1. 普通字符和11个元字符:
普通字符 匹配自身 abc abc
. 匹配任意除换行符”\n”外的字符 a.c abc
\ 转义字符,使后一个字符改变原来的意思 a.c;a\c a.c;a\c
* 匹配前一个字符0或多次 abc* ab;abc;cc
+ 匹配一个字符1次或无限次 abc+ abc;abccc
? 匹配一个字符0次或1次 abc? ab;abc
^ 匹配字符串开头,在多行模式中匹配每行的开头 ^abc abc
$ 匹配字符串末尾,在多行模式中匹配每行的末尾 abc$ abc
| 或,匹配|左右表达式任意一个,从左到右匹配,若|没有包括在()中,则它的范围是整个正则表达式 abc|def abcdef
{} {m}匹配前一个字符m次,{m,n}匹配前一个字符m~n次,若省略n,则匹配m至无限次 ab{1,2}c abc;abbbc
[] 字符集,对应的位置可以是字符集中的任意字符,字符集中的字符可逐个列出,也可给出范围,如[abc]或[a-c],[^abc]表示取反,即非abc。所有特殊字符在字符集都失去其特殊含义,用\转义恢复其特殊含义 a[bcd]e abe;ace;ade
() 被阔起来的表达式作为分组,从表达式左边开始没遇到一个分组的左括号”(“,编号+1,分组表达式作为一个整体,可接数量词。表达式中的|仅在该组有效 (abc){2};a(123|456)c abcabc;a456c

反斜杠作用:

  • 后跟元字符去除特殊功能,如特殊字符转义成普通字符
  • 后跟普通字符实现特殊功能,如预定义字符
  • 引号序列对应字组所匹配的字符串

2.预定义字符集

普通字符 字符含义 abc abc
\d 数字:[0-9] a\bc a1c
\D 非数字:[^\d] a\Dc abc
\s 匹配任何空白字符:[<空格>\t\r\n\f\v] a\sc a c
\w 匹配包括下划线在内的任何字符:[A-Za-z0-9_] a\wc abc
\W 匹配非字母字符,即匹配特殊字符 a\Wc a c
\A 仅匹配字符串开头,同^ \Aabc abc
\Z 仅匹配字符串结尾,同$ abc\Z abc
\b 匹配\w和\W之间,即匹配一个单词边界,也就是单词和空格间的位置,如’er\b’可匹配’never’中的’er’,但不能匹配’verb’中的’er’ \babc\b;a\b!bc 空格abc;空格a!bc
\B [^\b] a\Bbc abc

3.特殊分组用法

普通字符 字符含义 abc abc
(?P<name>) 分组除了原有的编号外再指定一个额外的别名 (?P<id>abc){2} abcabc
(?P=name) 引用别名为\的分组匹配到字符串 (?P<id>\d)abc(?P=id) 1abc15abc5
<number> 引用编号为\的分组匹配到字符串 (\d)abc\1 1abc15abc5

re模块中的常用函数

1.compile()
编译正则表达式模式,返回一个对象模式
格式:re.compile(pattern,flags=0)
pattern:编译时用的字符串表达式
flags:编译标志位,用于修改正则表达式的匹配方式,如是否区分大小写,多行匹配等。

常用flags:

标志 含义
re.S(DOTALL) 使匹配包括换行在内的所有字符
re.I(IGNORECASE) 使匹配对大小写不敏感
re.L(LOCALE) 做本地化识别(locale-aware)匹配
re.M(MULTILINE) 多行匹配,影响^和$
re.X(VERBOSE) 该标志通过给予灵活的格式以使正则表达式写得更易于被理解
re.U 根据Unicode字符集解析字符,这个标志影响\w,\W,\b,\B
1
2
3
4
5
6
7
import re
tt = "It is a good and cool day!"
rr = re.compile(r'\w*oo\w*')
print(rr.findall(tt)) #查找所有包含'oo'的单词
执行结果:
['good','cool']
  1. match()

决定RE是否在字符串刚开始的位置匹配
该方法并不是完全匹配,当pattern结束后,若string还有剩余字符,仍视为成功。若要完全匹配,则在表达式末尾加上边界匹配符’$’
格式:re.match(pattern,string,flags=0)

1
2
3
4
5
6
print(re.match('com','comwww.runcomoob').group())
print(re.match('com','Comwww.runcomoob',re.I).group())
执行结果:
com
com

  1. search()

格式:re.search(pattern,string,flags=0)
re.search函数会在字符串内查找模式匹配,只要找到第一个匹配然后返回,若字符串没有匹配,则返回None

1
2
3
4
print(re.search('\dcom','www.4comrunoob.5com').group())
执行结果:
2com

match和search一旦匹配成功,就是一个match object对象,而match object对象有以下方法:

  • group() 返回被 RE 匹配的字符串
  • start() 返回匹配开始的位置
  • end() 返回匹配结束的位置
  • span() 返回一个元组包含匹配 (开始,结束) 的位置
  • group() 返回re整体匹配的字符串,可以一次输入多个组号,对应组号匹配的字符串

a.group()
返回re整体匹配的字符串,

b.group(n,m)
返回组号为n,m所匹配的字符串,如果组号不存在,则返回indexError异常

c.groups()
返回一个包含正则表达式中所有小组字符串的元组,从 1 到所含的小组号,通常groups()不需要参数,返回一个元组,元组中的元就是正则表达式中定义的组

1
2
3
4
5
6
7
8
9
import re
a = "123abc456"
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0)) #123abc456,返回整体
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1)) #123
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2)) #abc
print(re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3)) #456
#group(1) 列出第一个括号匹配部分
#group(2) 列出第二个括号匹配部分
#group(3) 列出第三个括号匹配部分
  1. findall()
    re.findall遍历匹配,可获取字符串中所有匹配的字符串,返回一个列表
    格式:re.findall(pattern,string,flags=0)
    1
    2
    3
    4
    5
    p = re.compile(r'\d+')
    print(p.findall('o1n2m3k4'))
    执行结果如下:
    ['1', '2', '3', '4']
1
2
3
4
5
6
7
8
9
import re
tt = "Tina is a good girl, she is cool, clever, and so on..."
rr = re.compile(r'\w*oo\w*')
print(rr.findall(tt))
print(re.findall(r'(\w)*oo(\w)',tt))#()表示子表达式
执行结果如下:
['good', 'cool']
[('g', 'd'), ('c', 'l')]
  1. finditer()
    搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器,找到RE匹配的所有子串,并把它们作为一个迭代器返回
    格式:re.finditer(pattern,string,flags=0)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
iter = re.finditer(r'\d+','12 drumm44ers drumming, 11 ... 10 ...')
for i in iter:
print(i)
print(i.group())
print(i.span())
执行结果如下:
<_sre.SRE_Match object; span=(0, 2), match='12'>
12
(0, 2)
<_sre.SRE_Match object; span=(8, 10), match='44'>
44
(8, 10)
<_sre.SRE_Match object; span=(24, 26), match='11'>
11
(24, 26)
<_sre.SRE_Match object; span=(31, 33), match='10'>
10
(31, 33)
  1. split()
    按照能够匹配的子串将string分割后返回列表,可使用re.split来分割字符串,如re.split(r’\s+’,text)来将字符串按空格分割成一个单词列表
    格式:re.split(pattern,string[, maxsplit])
    maxsplit用于指定最大分割次数,不指定将全部分割
1
2
3
4
print(re.split('\d+','one1two2three3four4five5'))
执行结果如下:
['one', 'two', 'three', 'four', 'five', '']
  1. sub()
    使用re替换string中每一个匹配的子串后返回替换后的字符串
    格式:re.sub(pattern,repl,string,count)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import re
text = "JGood is a handsome boy, he is cool, clever, and so on..."
print(re.sub(r'\s+', '-', text))
执行结果如下:
JGood-is-a-handsome-boy,-he-is-cool,-clever,-and-so-on...
其中第二个函数是替换后的字符串;本例中为'-'
第四个参数指替换个数。默认为0,表示每个匹配项都替换
```
re.sub还允许使用函数对匹配项的替换进行复杂处理,如
re.sub(r'\s', lambda m:'[' + m.group(0) + ']',text,0)将字符串中的空格' '替换为'[]'
``` python
import re
text = "JGood is a handsome boy, he is cool, clever, and so on..."
print(re.sub(r'\s+', lambda m:'['+m.group(0)+']', text,0))
执行结果如下:
JGood[ ]is[ ]a[ ]handsome[ ]boy,[ ]he[ ]is[ ]cool,[ ]clever,[ ]and[ ]so[ ]on...
  1. subn()
    返回替换次数
    格式:subn(pattern,repl,string,count=0,flags=0)
1
2
3
4
5
6
7
8
print(re.subn('[1-2]','A','123456abcdef'))
print(re.sub("g.t","have",'I get A, I got B ,I gut C'))
print(re.subn("g.t","have",'I get A, I got B ,I gut C'))
执行结果如下:
('AA3456abcdef', 2)
I have A, I have B ,I have C
('I have A, I have B ,I have C', 3)

re.match与re.search与re.findall区别:

re.match只匹配字符串的开始,如字符串开始不符合正则表达式,则匹配失败,函数返回None;
re.search匹配整个字符串,直到找到一个匹配

1
2
3
4
5
6
7
8
9
10
11
a=re.search('[\d]',"abc33").group()
print(a)
p=re.match('[\d]',"abc33")
print(p)
b=re.findall('[\d]',"abc33")
print(b)
执行结果:
3
None
['3', '3']

贪婪匹配与非贪婪匹配

*?,+?,??,{m,n}?
前面*,+,?等都是贪婪匹配,即尽可能匹配,后面加?号使其变成惰性匹配

1
2
3
4
5
6
7
8
a = re.findall(r"a(\d+?)",'a23b')
print(a)
b = re.findall(r"a(\d+)",'a23b')
print(b)
执行结果:
['2']
['23']

1
2
3
4
5
6
7
8
a = re.match('<(.*)>','<H1>title<H1>').group()
print(a)
b = re.match('<(.*?)>','<H1>title<H1>').group()
print(b)
执行结果:
<H1>title<H1>
<H1>
1
2
3
4
5
6
7
8
9
a = re.findall(r"a(\d+)b",'a3333b')
print(a)
b = re.findall(r"a(\d+?)b",'a3333b')
print(b)
执行结果如下:
['3333']
['3333']
若前后均有限定条件的时候,就不存在什么贪婪模式了,非匹配模式失效

用flags时应注意:

1
2
3
4
print(re.split('a','1A1a2A3',re.I))#输出结果并未能区分大小写
#re.split(pattern,string,maxsplit,flags)默认是四个参数
当我们传入的三个参数的时候,系统会默认re.I是第三个参数,所以就没起作用
如果想让这里的re.I起作用,写成flags=re.I即可

实践

  • 匹配电话号码
1
2
p = re.compile(r'\d{3}-\d{6}')
print(p.findall('010-628888'))
  • 匹配IP
    1
    2
    re.search(r"(([01]?\d?\d|2[0-4]\d|25[0-5])\.){3
    }([01]?\d?\d|2[0-4]\d|25[0-5]\.)","192.168.1.1")