正则表达式

#行定界符 (^行的开始 $行的结尾)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
实例一:

import re
subject = "html、htm"
pattern ='^htm'
matches = re.findall(pattern,subject)
print(matches)


示例二:
import re
lines = ["Hello,world.","hello,world.","ni hao","Hello Tom"]
results = []
for line in lines:
if re.findall(r'^H',line):
results.append(line)
print(results)

results=[]
for line in lines:
if re.findall(r'm$',line):
results.append(line)
print(results)

# 单词定界符 (\b单词边界,\B非单词边界)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
示例一:
import re
subject = "html,htm"
pattern = r'\bhtm\b'
matches = re.findall(pattern,subject)
print(matches)

示例二:
import re
text = "apple took itake tattle tabled tax yed temperate"
print(re.findall(r'\bta.*\b',text))
print(re.findall(r'\bta\S*?\b',text))
print(re.findall(r'\bta\S*?ed\b',text))

实例三:
import re
text = "phone phoneplus iphone telephone telegram"
words = text.split()
results = []
for word in words:
if re.findall(r'\Bphone',word):
results.append(word)
print(results)

# 字符类(字符集,就是一个字符列表,就是匹配字符列表中的任意一个字符,使用[…]可以定义字符类)

1
2
3
4
5
6
7
8
9
10
11
实例一:
import re
pattern = '[hH][tT][mM][lL]'
str = "html,HTML,Html,html或HTml"
print(re.findall(pattern,str))

示例二:
import re
pattern = '[-\[\]^.*]'
str = "[]-\.*^"
print(re.findall(pattern,str))

选择符(可实现选择性匹配。使用‘|’可以定义选择模式,类似python中的逻辑或)

1
2
3
4
5
import re
pattern = 'h|Html'
str = "[]-\.*^,Html"
print(re.findall(pattern,str))

#范围符(需要列举所有可选字符时,当遇到较多时就比较麻烦,不过可以用-进行范围的选择)

1
2
3
4
5
pattern = '[a-z]'
pattern = '[A-Z]'
pattern = '[0-9]'
pattern = '[\u4e00\u9fa5]'
pattern = '[\x00-\xff]'

排除符(在字符类,除了范围符外,还有一个元字符:排除符(^)。

将“^”放到方括号内最左侧,表示排除符列表,也就是将反转该集合的意义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#类似python运算中的逻辑非

pattern = '[^0-9]'
pattern = '[^\x00-\xff]'


#限定符

示例一;

import re
subject = "goooooooole"
pattern = "o{1,4}"
matches = re.search(pattern,subject)
matches = matches.group()
print(matches)

示例二:(在字符模式中为{1,4}限定符补加一个“?”后缀,定义该限定符为非贪婪匹配,则最后仅匹配字符串“goooooglemport re
subject = "goooooooogle中的第一个o"
pattern = "o{1,4}?" # 非贪婪匹配
matches = re.search(pattern,subject)
matches = matches.group()
print(matches)

任意字符(点号(.)能匹配除换行符\n外的任意单字符。如果要匹配点号(.)自己,需要使用“\”进行转义)

1
2
3
4
5
6
import re
subject = "goooooooooogle"
pattern = ".{1,6}"
matches = re.search(pattern,subject)
matches = matches.group()
print(matches)

转义字符(转义字符“\”能够将特殊字符变为普通字符,与python字符串中的转义字符类似)

1
2
3
4
5
6
7
#如果把特殊字符放在中括号内定义字符集,也能把特殊字符转换为普通字符,如[*]等效于“\*”,都可以用来匹配字符"*"
import re
subject = "127.0.0.1"
pattern = "([0-9]{1,3}\.?){4}"
matches = re.search(pattern,subject)
matches = matches.group()
print(matches)

小括号

在正则表达式中,小括号有两个作用,简单的说明如下:

1,定义独立单元

1
2
3
4
5
#小括号可以改变选择符和限定符的作用范围

pattern = '(h|H)tml'
pattern = 'goo{1,3}'

2,分组

1
2
3
4
5
6
7
8
9
10
11
小括号的第二个作用就是分组,即定义子表达式,
子表达式相当于一个独立的正则表达式,后面学到的反向引用与子表达式有直接关系。
子表达式能够临时存储其匹配的字符,然后可以在后面进行应用
正则表达式允许多次分组,嵌套分组,从表达式左侧开始第一个左括号“(”的编号为1,然后遇到一个分组的左括号“(”,编号就加一

pattern = '(a(b(c)))'

除了默认标号外,也可以为分组定义一个别名。语法格式:
(?P<name>...)
例如,下面表达式可以匹配字符串abcabcabc
(?P<id>abc{3})

反向引用

1
2
3
4
5
6
7
8
9
10
11
import re
subject = "abcdebbcde"
pattern = r"([ab])\1" #其中“\1”表示“\n”,n表示为一个标识特定缓冲区的编号
matches = re.search(pattern,subject)
matches = matches.group()
print(matches)

对于正则表达式:r"([ab])\1",也可用别名进行引用,语法格式如下:
(?P=name)
例如,下面的表达式可以匹配字符串1a1,2a2,3a3
(?P<num>\d)a(?P=num)

特殊构造

1,不分组

1
2
3
4
5
下面语法可以设计小括号不分组,仅作为独立单元用于“|”或重复匹配
(?:...)
例如,下面表达式仅用于界定逻辑作用范围,不用来分组:
(?:\w)*
(?:html|htm)

2,定义匹配模式

1
2
3
4
5
6
使用下面语法可以定义表达式的匹配模式。
(?aiLmsux)正则表达式字符串
aiLmsux中的每个字符代表一种匹配模式
(?aiLmsux)只能够用在正则表达式的开头,可以多选。
例如下面表达式可以匹配a,也可以匹配A
(?ia)

3,注释

1
2
3
4
使用下面语法可以在正则表达式中添加注释信息,“#”后面的文本作为注释内容将被忽略
(?#注释信息)
在下面表达式中添加一句注释,以便表达式阅读和维护
a(?#匹配字符abc)bc

4,前正瞻

1
2
3
4
使用下面的语法可以定义表达式后面必须满足特定的匹配条件
(?...)
例如,下面表达式仅匹配后面包含数字的字母a
a(?=\d)

5,负前瞻

1
2
3
4
使用下面的语法可以定义表达式后面必须不满足特定的匹配条件。
(?!...)
例如,下面表达式仅匹配后面不包含数字的字母a
a(?!\d)

6,正回顾

1
2
3
4
5
使用下面的语法可以定义表达式前面必须满足特定的匹配条件
(?<=...)
例如,下面表达式仅匹配前面包含数字的字母a
(?<=\d)a

7,负回顾

1
2
3
4
使用下面的语法可以定义表达式后面必须不满足特定的匹配条件
(?<!...)
例如,下面表达式仅匹配前面不包含数字的字母a
(?<!\d)a

8,条件匹配

1
2
3
4
5
6
7
8
9
10
11
使用下面语法可以定义条件匹配表达式。
(?(id/name)yes-pattern | no-pattern),其中id表示分组编号,name表示分组的别名,
如果对应的分组匹配到字符,则选择yes-pattern子表达式执行匹配;
如果对应的分组没有匹配到字符,则选择no-pattern子表达式执行匹配,|no-pattern可以直接省略,
写成:
(?(id/name)yes-pattern)

import re
pattern = '((<)?/?\w+(?(2)>))'
str = "<b>html</b><span>html</span>"
print(re.findall(pattern,str))

匹配模式

1
2
3
4
5
6
import re
subject = 'My user name is Css888!'
pattern = r'css\d{3}'
matches = re.search(pattern,subject,re.I|re.M)
matches = matches.group()
print(matches)

案例:匹配QQ号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
腾讯QQ号
模式分析:
QQ好的首位号码是不会以0开始的,可以用[1-9]匹配
QQ号是从10000开始,至少有5位,后四位可用[0-9]{4,}匹配

import re
subject1 = "12345"
subject2 = "1234"
subject3 = "01234"
subject4 = "12345789"
pattern = "[1-9][0-9]{4,}"
print(re.findall(pattern,subject1))
print(re.findall(pattern,subject2))
print(re.findall(pattern,subject3))
print(re.findall(pattern,subject4))

#案例 匹配货币的输入格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22


货币可以为一个0或者以0开头,如果为负数,可用^(0|-?[1-9][0-9]*)$
货币通常情况下不为负数,当支持小数时,小数点后至少有一位数值或者两位,可用^[0-9]+(\.[0-9]{1,2})?$
输入货币是能会需要用到逗号分隔,可以设置1~3个数字,后面跟着任意个逗号+3个数字,
其中逗号可选,可用 ^([0-9]+[0-9]{1,3}(,[0-9]{3})*)(\.[0-9]{1,2})?$

import re
subject1 = "12,345.00"
subject2 = "10."
subject3 = "-1234"
subject4 = "123,456.789"
subject5 = "10.0"
subject6 = "0123"

pattern = "^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(\.[0-9]{1,2})?$"
print(re.findall(pattern,subject1))
print(re.findall(pattern,subject2))
print(re.findall(pattern,subject3))
print(re.findall(pattern,subject4))
print(re.findall(pattern,subject5))
print(re.findall(pattern,subject6))

使用re模块

正则表达式对象

pattern对象是一个编译好的正则表达式,通过pattern提供的一系列方法可以对文本进行匹配操作。Pattern不能直接实例化对象,可以直接使用re.compile函数构造对象。

1
2
3
re.compile(pattern,[,flags])
#pattern:一个正则表达式字符串
#flags:可选,表示匹配模式,如忽略大小写,多行模式等

示例一:使用compile()函数构建一个正则表达式对象,用来匹配一个或多个数字,然后调用正则表示式对象的match()方法,检查字符串’a1b2c3d4e5f6’中起始位置是否为数字,也可以传递第二个参数设置起始位置。

1
2
3
4
5
6
7
import re 
pattern = re.compile(r'\d+')
subject = 'a1b2c3d4e5f6'
m = pattern.match(subject)
print(m)
m = pattern.match(subject,1)
print(m)

扩展:pattern 提供了几个可读属性用于获取正则表达式相关信息,说明如下

pattern :正则表达式的字符串表示

flags: 以数字形式返回匹配模式

group:表达式中分组的数量

groupindex:以表达式中有别名的组的别名为键,以该组对应的编号为值的字典,没有别名的组不包括在内

示例二:定义一个正则表达式对象,然后使用pattern,flags,groups,和groupindex访问正则表达式的字符串表示,匹配模式,分组数和别名字典集。

1
2
3
4
5
6
import re
P = re.compile(r'(\w+)(\w+)(?P<a>.*)',re.DOTALL)
print("P.pattern:",P.pattern)
print("P.flags:",P.flags)
print("P.groups:",P.groups)
print("P.groupindex:",P.groupindex)

pattern.match(string[,pos[,endpos]]):该方法与re.match()函数功能相同,它能够从参数string字符串的pos下标处起尝试匹配pattern,如果匹配成功,则放回一个Match对象;如果无法匹配,或者匹配未结束就已到达endpos下标位置,则返回None。

serach(string[,pos[,endpos]]):该方法与re.match()函数功能相同,它能够从参数string字符串的pos下标处起尝试匹配pattern,如果无法匹配,则将pos加1后重新尝试匹配,直到pos=endpos时;如果匹配成功,则放回一个Match对象;。

示例三:将正则表达式编译成Pattern对象,然后使用serach()查找匹配的字符串,不存在能匹配的字符串时将放回None,本例使用match()将无法成功匹配。

1
2
3
4
5
6
import re 
subject = 'www.mysize.cn'
pattern = re.compile(r'cn')
match = pattern.search(subject)
if match:
print(match.group())

findall(string[,pos[,endpos]]):该方法与re.findall()函数的功能相同,搜索string,以列表形式返回全部能匹配的子串。

finditer(string[,pos[,endpos]]):该方法与findall()函数的功能相同,搜索string,返回一个顺序访问每一个匹配结果(Match 对象)的迭代器。

示例四:将正则表达式编译成Pattern对象,然后使用finditer()查找匹配的子串,并返回一个匹配对象的迭代器,最后使用for语句遍历迭代器,输出每一个匹配信息。

1
2
3
4
5
6
import re 
subject = 'Cats are smarter than dogs'
pattern = pattern.compile(r'\w+',re.I)
iter = pattern.finditer(subject)
for m in iter:
print(m.group())

sub(repl,string,[,count]):该方法与re.sub()函数的功能相同,使用参数repl替换string中每一个匹配的子串,然后返回替换后的字符串。

subn(repl,string,[,count]):该方法与re.sub()函数的功能相同,但是返回替换字符串,以及替换的次数

示例五:定义正则表达式对象,匹配字符串中非单词类字符,然后使用subn()f方法把它们替换为下划线,同时会返回替换的次数。

1
2
3
4
5
import re
subject = 'Cats are smarter than dogs'
pattern = re.compile(r'\W+')
matches = pattern.subn("_",subject)
print(matches)

split(string[,maxsplit]):该方法与re.spilt函数的功能相同,按照能够匹配的字符串将string分隔,返回列表。maxsplit用于指定最大分隔次数,不指定将全部分隔。

匹配对象

Match对象表示一次匹配的结果,包含本次匹配的相关信息,可以使用Match对象的属性和方法来获取这些信息。

1,属性

string:匹配的文本字符串。

re:匹配时使用的Pattern对象

pos:在文本中正则表达式开始匹配的索引位置

endpos:在文本中正则表达式结束匹配的索引位置

lastindex:最后一个被捕获的分组在文本中的索引。如果没有被捕获的分组,则值为None

lastgroup:最后一个被捕获分组的别名。如果这个分组没有别名或者没有被捕获的分组,则值为None。

2,方法

group([group1,…]):获取一个或多个分组匹配的字符串,如果指定多个参数时,将以元组的形式返回。参数可以使用编号,也可以使用别名。编号0代表整个匹配结果。不填写参数时,返回group(0)。如果没有匹配的分组,则返回None;如果执行了多次匹配,则返回最后一次匹配的分组结果。

groups():以元组形式返回全部分组匹配的字符串。相当于调用group(1,2,…,last)。

groupdict():以字典的形式返回定义别名的分组信息,字典元素为一个以别名为键,以该组匹配的子串为值,没有别名的组不包含在内。

start([group]):返回指定的组截获的子串在string中的起始索引(子串第一个字符的索引)。group默认值为0。

end([group]):返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引+1)。group默认值为0。

span([group]):返回(start(group),end(group))。

expand(template):将匹配到的分组代入template中,然后返回。template中可以使用\id或\g,\g引用分组,但不能使用编号0。\id与\g是等价的,但\10将被认为是第10个分组,如果相表达\1之后是字符‘0’,只能使用\g<1>0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import re
m = re.match(r'(\w+)(\w+)(?P<sign>.*)','hello world!')
print("m.string:",m.string)
print("m.re:",m.re)
print("m.pos:",m.pos)
print("m.endpos:",m.endpos)
print("m.lastindex:",m.lastindex)
print("m.lastgroup:",m.lastgroup)
print("m.group(1,2):",m.group(1,2))
print("m.groups():",m.groups())
print("m.groupdict():",m.groupdict())
print("m.start(2):",m.start(2))
print("m.end(2):",m.end(2))
print("m.span(2):",m.span(2))
print(r"m.expand(r'\2\1\3'):",m.expand(r'\2\1\3'))

运行结果:
m.string: hello world!
m.re: re.compile('(\\w+)(\\w+)(?P<sign>.*)')
m.pos: 0
m.endpos: 12
m.lastindex: 3
m.lastgroup: sign
m.group(1,2): ('hell', 'o')
m.groups(): ('hell', 'o', ' world!')
m.groupdict(): {'sign': ' world!'}
m.start(2): 4
m.end(2): 5
m.span(2): (4, 5)
m.expand(r'\2\1\3'): ohell world!

匹配字符串

在python中,可以使用re模块提供的match(),search(),findall()和finditer()等方法来匹配字符串。

1,使用match()

使用match()函数可以从字符串的起始位置开始匹配一个正则表达式,如果不是起始位置匹配,match()将返回None。

1
2
3
4
re.match(pattern,string,flags)=0
#pattern:正则表达式
#string :字符串
#flags:匹配模式,用于控制正则表达式的匹配模式,如是否区分大小写,多行匹配。

注意:match函数适合验证字符串,检验字符串是否符合特定格式

示例一:使用match()函数匹配字符串中的www起始字符。

1
2
3
4
5
6
7
import re 
subject = 'www.mysite.cn'
pattern = 'www'
matches = re.match(pattern,subject)
print(matches)
print(matches.group)

示例二:使用match()函数匹配一句话,然后使用小括号语法获取不同部分的单词,同时定义匹配模式为re.M|re.I,及不区分大小写和允许多行匹配。

1
2
3
4
5
6
7
8
9
10
11
import re 
subject = 'Cat are smarter than dogs'
pattern = '(.*) are (.*?) (.*)'
matches = re.match(pattern,subject,re.M|re.I)
if matches:
[print(matches.groups())]
[print(matches.groups(1))]
[print(matches.groups(2))]
[print(matches.groups(3))]
else:
print("No match!")

2,使用search()

使用search()函数可以使用获取第一个成功的的匹配,该函数没有起始位置限制,语法格式如下:

re.matches(pattern,string,flags=0)

参数如下:

pattern :正则表达式

string:要匹配的字符串

flags:匹配模式,用于控制正则表达式的匹配方式,如是否区分大小写,多行匹配等。

如果匹配成功,search()将返回一个匹配对象;否则返回None,然后使用匹配对象的group(num)或者groups()方法来读取匹配的字符串。

示例三:可以使用search()来替代match()函数

1
2
3
4
5
6
import re 
subject = 'www.mysize.cn'
pattern = 'www'
matches = re.search(pattern,subject)
print(matches)
print(matches.group())

示例四:针对示例2,也可以使用search()函数来替代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import re 
subject = 'Cat are smarter than dogs'
pattern = '(.*) are (.*?) (.*)'
matches = re.search(pattern,subject,re.M|re.I)
if matches:
print(matches.groups())
print(matches.group(1))
print(matches.group(2))
print(matches.group(3))
else:
print("No match!")

运行结果:
('Cat', 'smarter', 'than dogs')
Cat
smarter
than dogs

注意:

re.match()与re.search()的用法和功能相似,它们都仅执行一次匹配,不同点为re.match()只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,返回None;而re.match()可以检察整个字符串,直到能够找到一个匹配。

3,使用findall()

使用findall()函数可以在指定字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则放回空列表。语法格式如下:

1
2
3
4
re.match(pattern,subject,flags=0)
#pattern:正则表达式
#string :字符串
#flags:匹配模式,用于控制正则表达式的匹配模式,如是否区分大小写,多行匹配。

示例五:针对示例2,本示例使用findall()函数找出字符串中所有的单词。

1
2
3
4
5
6
7
8
import re 
subject = 'Cat are smarter than dogs'
pattern = r'\b\w+?\b'
matches = re.findall(pattern,subject,re.M|re.I)
if matches:
print(matches)
else:
print("No match!")

示例六:比较了同一个表达式添加小括号与不添加小括号所匹配的结果的不同

1
2
3
4
5
6
7
8
import re
string = "a b c d"
regex1 = re.compile("((\w)\s+\w)")
print(regex1.findall(string))
regex2 = re.compile("(\w)\s+\w")
print(regex2.findall(string))
regex3 = re.compile("(\w)\s+\w")
print(regex3.findall(string))

4,使用finditer()

finditer()与findall()函数用法相同,但是finditer()返回的结果为迭代器.finditer()与正则表达式对象的finditer()方法的返回结果相同,用法基本相似。

替换字符串

使用sub()和subn()函数可能替换字符串中的匹配项,语法格式如下:

1
2
re.sub(pattern,repl,string,count=0,flags=0)
re.subn(pattern,repl,string,count=0,flags=0)

sub函数返回替换后的字符串,subn()函数以元组的形式返回替换后的字符串和替换次数

1
2
3
4
5
#pattern:正则表达式
#string :字符串
#flags:匹配模式,用于控制正则表达式的匹配模式,如是否区分大小写,多行匹配。
#repl:替换的字符串,也可为一个函数
#count:替换匹配后替换的最大次数,默认0表示替换所有的匹配。

示例一:使用sub()替换日期字符串中的连字符,使用斜杠分隔日期中的年月日

1
2
3
4
5
import re 
subject = '2019-10-10'
pattern = r'-'
matches = re.sub(pattern,"/",subject)
print(matches)

示例二:在sub()中传递替换函数,通过替换函数对匹配的字符执行复杂的操作

1
2
3
4
5
6
7
8
9
import re 
def f(matched):
value = int(matched.group(1))
return str(value * 2) + " "

subject = '123456789'
pattern = '(\d)'
matches = re.sub(pattern,f,subject)
print(matches)

分割字符串

使用split()函数可以按匹配的子串分隔字符串,并返回分隔的元素列表。语法格式如下:

1
2
3
4
5
re.split(pattern,string[,maxsplit=0,flags=0])
#pattern:正则表达式
#string :字符串
#maxsplit:分隔次数,默认为0,既不限次数。
#flags:匹配模式,用于控制正则表达式的匹配模式,如是否区分大小写,多行匹配。

示例:使用split()函数分隔一句话,以空格为分隔符,提取该字符串中包含的单词。

1
2
3
4
5
import re 
subject = 'Cat are smarter than dogs'
pattern = r'\s+'
matches = re.split(pattern,subject)
print(matches)

案例:

1,过滤敏感词

利用re模块中的sub()函数将文档语句中含有敏感词汇替换成”*”。案例代码如下:

1
2
3
4
5
6
7
8
9
import re 
def filterwords(keywords,text):
return re.sub('|'.join(keywords),'**',text)
keywords = ('上海','外滩')
text = '上海外滩很漂亮'
print(filterwords(keywords,text))

运行结果:
****很漂亮

2,分组匹配

分组匹配主要用到的函数包括group(),groups(),groupdict()。group()方法用于访问分组匹配的字符串;groups()方法是把匹配结果以元组的方式返回,是一个元组;groupdict()方法是把匹配结果以字典的方式返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import re 
pattern = r'[a-zA-Z0-9]{9,11}@(163|122|qq).com'
subject = '987654321@163.com'
matches = re.match(pattern,subject)
print(matches.groups())


pattern = r'[a-zA-Z0-9]{9,11}@(163|122|qq).com'
subject = '987654321@qq.com'
matches = re.match(pattern,subject)
print(matches.groups())

pattern = r'[a-zA-Z0-9]{9,11}@(.*),[\w]*@(.*),[\w]*@(.*)'
subject = '987654321@163.com,abcdefgh@qq.com,123456789@qq.com'
matches = re.match(pattern,subject)
print(matches.group())
print(matches.groups())


#定义分组匹配的正则表达式并起名,匹配用户名和邮箱
pattern = r'(?P<username>\w*) "(?P<mail>[a-zA-Z0-9]{6,11}@[a-z0-9]*\.[a-z]{1,3})"'
subject = 'administrator "admin12345@163.com"'
matches = re.match(pattern,subject)
print(matches.groupdict())

运行结果:
('163',)
('qq',)
987654321@163.com,abcdefgh@qq.com,123456789@qq.com
('163.com', 'qq.com', 'qq.com')
{'username': 'administrator', 'mail': 'admin12345@163.com'}

3,匹配手机号码

常见的手机号格式为11位数字,其中前两位数字可以为13,14,15,17,18,19或者前三位以166开头等,如13012345678。

模式分析

以13,14,15,17,18开头的前三位,可用13[0-9]|14[0-9]|15[0-9]|17[0-9]|18[0-9]

以166开头,可以直接用166表示。

以19开头的前3位,可用19[8|9]。

后8位的数字,可用d{8}。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import re
regex = r'(13[0-9]|14[0-9]|15[0-9]|166|17[0-9]|18[0-9]|19[8|9])\d{8}$'
pattern = re.compile(regex,re.I)
phone1 = '13012345678'
phone2 = '19912345678'
phone3 = '15812345678'
phone4 = '12812345678'
matches1 =pattern.match(phone1)
matches2 =pattern.match(phone2)
matches3 =pattern.match(phone3)
matches4 =pattern.match(phone4)
print(not not matches1)
print(not not matches2)
print(not not matches3)
print(not not matches4)

4,匹配身份证

身份证可以分为一代和二代,一代身份证号码共计15位数字,二代身份共计18位数字,位数可能包括x或X,如11010120000214842x。一代身份证比较少见,本例仅匹配二代身份证。二代身份证号码组成:

6位数字地址码+8位数字出生日期码+3位顺序码+1位校验码

1
2
3
4
#6位数字地址码:根据省,市,区,县分配,各地略有不同,可以使用[1-9][0-9]{5}表示
#8位数字出生日期码:4位年+2位月+2位日,可以使用19[0-9]{2}|20[0-9]{2},2位月可以使0[1-9]|1[0-2],2位日可以使用0[1-9]|1[0-9]|2[0-9]|3[0-1]
#3位顺序码:可用[0-9]{3}
#1位校验码:可用[0-9xX]

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import re
regex = r"^[1-9][0-9]{5}(?P<year>19[0-9]{2}|20[0-9]{2})(?P<month>0[1-9]|1{0-2})(?P<day>0[1-9]|1[0-9]|2[0-9]|3[01])[0-9]{3}[0-9xX]$"
pattern = re.compile(regex,re.I)
id1 = '11010120000214842x'
id2 = '11010120000214842X'
id3 = '110101200002148421'
id4 = '11010120000214842'
matches1 =pattern.match(id1)
matches2 =pattern.match(id2)
matches3 =pattern.match(id3)
matches4 =pattern.match(id4)
print(not not matches1)
print(not not matches2)
print(not not matches3)
print(not not matches4)

案例实战:

1,匹配十六进制颜色值

表示一个十六进制字符,可以使用字符串[0-9a-fA-F]来匹配。

其中字符可以出现3次或者6次,需要使用量词和分支结构。

使用分支结构时,需要注意顺序。

1
2
3
4
5
import re 
regex = '#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}'
pattern = re.compile(regex,re.I)
string = "#ffbbad #Fc01DF #FFF #ffE"
print(pattern.findall(string))

2,匹配时间

共4位数字,第一位数字可以为[0-2]

当第1位为“2”时,第2位可以位[0-3],其他情况时,第2位为[0-9]。

第三位数字为[0-5],第4位为[0-9]

1
2
3
4
5
6
7
import re 
regex = '^([01][0-9]|[2][0-3]):[0-5][0-9]$'
pattern = re.compile(regex)
print(not not pattern.match("23:59"))
print(not not pattern.match("02:07"))
print(not not pattern.match("43:12"))
print(not not pattern.match("10:10"))
1
2
3
4
5
6
7
import re 
regex = '^(0?[0-9]|1[0-9]|2[0-3]):(0?[0-9]|[1-5][0-9])$'
pattern = re.compile(regex)
print(not not pattern.match("7:9"))
print(not not pattern.match("23:59"))
print(not not pattern.match("02:07"))
print(not not pattern.match("24:07"))

3,匹配日期

常见日期格式为yyyy-mm-dd,如2018-06-10

年,4位数字即可,可用19[0-9]{2}|20[0-9]{2}。

月,共12个月,分为2种情况”01”、”02”、”…”、”09”和”10”、”11” 、”12”,可用[1-9] | 0[1-9] | 1[0-2]。

日,最后31天,可用(0[1-9] | [12][0-9] | 3[01]])

1
2
3
4
5
6
import re 
regex = '^19[0-9]{2}|20[0-9]{2}-(0?[1-9]|1[0-2])-(0?[1-9]|[12][0-9]|3[01])$'
pattern = re.compile(regex)
print(not not pattern.match("2019-06-10"))
print(not not pattern.match("2019-6-1"))
print(not not pattern.match("2019-16-41"))

4,匹配成对的HTML标签

成对的HTML标签的格式如下:

1
2
<title>标题文本</title>
<p>段落文本</p>

匹配一个开标签,可以使用正则<>+>。

匹配一个闭标签,可以使用]+>。

要匹配成对标签,就需要使用反向引用,其中开标签<[\^>+]>改成<(\w+)>*>,使用小括号的目的是后面使用反向引用,闭标签使用了反向应用

[\d\D]表示这个字符是数字或者不是数字,因此也就匹配任意字符

1
2
3
4
5
6
import re 
regex = r'<(\w+)[^>]*>[\d\D]*<\/\1>'
pattern = re.compile(regex,re.I)
print(not not pattern.match("<title>标题文本</title>"))
print(not not pattern.match("<p>段落文本</p>"))
print(not not pattern.match("<div>非法嵌套</p>"))

5,匹配物理路径

整体模式是盘符:\文件夹\文件夹\文件夹

其中匹配”F:\”,需要使用[a-zA-Z]:\,盘符不区分大小写。注意,” \ “字符需要转义。

文件名或者文件夹名,不能包含一些特殊字符,此时需要排除字符类\\:*<>|"?\r\n/"来表示合法字符。

名字不能为空名,至少有一个字符,也就是要使用量词”+”。因此匹配“文件夹\”,可用\\:*<>|"?\r\n/+\。

“文件夹\”可以出现任意次数,就是(\\:*<>|"?\r\n/+\)*。其中括号表示其内部正则是一个整体。

路径的最后一部分可以是”文件夹”,没有”\”,因此需要添加(\\:*<>|"?\r\n/+)?。

最后拼接成一个比较复杂的正则表达式。

1
2
3
4
5
6
import re 
regex = r'^[a-zA-Z]:\\([^\\:*<>|"?\r\n/]+\\)*([^\\:*<>|"?\r\n/]+)?$'
pattern = re.compile(regex,re.I)
print(not not pattern.match("F:\\python\\regex\\index.html"))
print(not not pattern.match("F:\\python\\regex\\"))
print(not not pattern.match("F:\\"))

6,货币数字的千位分隔符表示

货币数字的千位分隔符格式:如”12345678”表示为”12,345,678”。

【操作步骤】

第1步,根据千位把相应的位置替换成”,”,以最后一个逗号为例,解决方法:(?=\d{3}$)。

1
2
3
4
5
6
7
8
9
import re 
regex = r'(?=\d{3}$)'
pattern = re.compile(regex)
string = "123456"
string = pattern.sub(',',string)
print(string)

运行结果:
123,456

其中,(?=\d{3}$)匹配\d{3}前面的位置,而\d{3}$匹配的是目标字符串最后的3位数字。

第2步,确定所有的逗号。因为逗号出现的位置要求后面3个数字一组,也就是\d{3}至少出现一次。此时可以使用量词”+”。

1
2
3
4
5
6
7
8
9
import re 
regex = r'(?=(\d{3})+$)'
pattern = re.compile(regex)
string = "12345678"
string = pattern.sub(',',string)
print(string)

运行结果:
12,345,678

第3步,匹配其他数字,会发现问题如下:

1
2
3
4
5
6
import re 
regex = r'(?=(\d{3})+$)'
pattern = re.compile(regex)
string = "123456789"
string = pattern.sub(',',string)
print(string)

上面正则表达式从结尾往前数,只要是3的倍数,就把前面的位置替换成逗号。如何解决匹配的位置不能是开头?

第4步,匹配开头可以使用”^”,但要求该位置不是开头,可以考虑使用(?<!^)。

1
2
3
4
5
6
import re 
regex = r'(?<!^)(?=(\d{3})+$)'
pattern = re.compile(regex)
string = "123456789"
string = pattern.sub(',',string)
print(string)

第5步,如果把”12345678 123456789”替换成”12,345,678 123,456,789”。此时需要修改正则表达式,需要把里面的开头”^”和结尾”$”修改成”\b”。实现代码如下:

1
2
3
4
5
6
import re 
regex = r'(?<!\b)(?=(\d{3})+\b)'
pattern = re.compile(regex)
string = "123456789"
string = pattern.sub(',',string)
print(string)

其中,(?<!\b)要求当前是一个位置,但不是\b前面的位置,其实(?<!\b)说的就是\B。因此,最终正则表达式就变成了\B(?=(\d{3})+\b)。

第6步,进一步格式化。千分符表示法一个常见的应用就是货币格式化。例如:

1
1888

格式化为:

1
$ 1888.00

有了前面的内容,可以很容易实现。

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
import re

def format(num):
string = '{:.2f}'.format(num)
regex = r'\B(?=(\d{3}) + \b)'
pattern = re.compile(regex)
string = pattern.sub(',',string)
return "$" + string

print(format(1888))
print(format(234345.456))

7,验证密码

密码长度一般为6~12位,由数字,小写字符和大写字母组成,但必须至少包括两种字符。如果写成多个正则表达式来判断比较容易,但要写成一个正则表达式就比较麻烦。

【操作步骤】

第1步,化简思路。不必考虑”但必须至少包括两种字符”条件,可以这样来实现。

1
regex = '^[0-9A-Za-z]{6-12}$'

第2步,判断是否包含某一种字符。

假设要求必须包括数字,此时可以使用(?=.*[0-9])。因此,正则变成如下:

1
regex = '(?=.*[0-9])^[0-9A-Za-z]{6,12}$'

第3步,同时包括具体两种字符。

假设同时包括数字和小写字母,可以使用(?=.[0-9])(?=.[a-z])^[0-9A-Za-z]{6,12}$,因此,正则表达式就变成如下:

1
regex = '(?=.*[0-9])(?=.*[a-z])^[0-9A-Za-z]{6,12}$'

第4步,把原题变成下列几种情况之一。

1
2
3
4
#同时包含数字和小写字母
#同时包含数字和大写字母
#同时包含小写字母和大写字母
#同时包含数字,小写字母和大写字母

以上4种情况是或的关系,实际上可以不用第4种情况。最终实现代码如下:

1
2
3
4
5
6
7
8
9
10
import re 
regex = r'((?=.*[0-9])(?=.*[a-z]) | (?=.*[0-9])(?=.*[A-Z]) | (?=.*[a-z])(?=.*[AZ]))^[0-9A-Za-z]{6,12}$'
pattern = re.compile(regex)
print(not not pattern.match("1234567"))
print(not not pattern.match("abcdef"))
print(not not pattern.match("ABCDEFGH"))
print(not not pattern.match("ab23C"))
print(not not pattern.match("ABCDEF234"))
print(not not pattern.match("abcdEF234"))

【模式分析】

上面的正则表达式看起来比较复杂,只要理解了第2步,其余就全部理解了。

1
'(?=.*[0-9])^[0-9A-Za-z]{6,12}$'

对于这个正则表达式,只需要弄明白(?=.*[0-9])^即可。

分开来看就是(?=.*[0-9])和”^”。

表示开头前面还有个位置(当然也是开头,及同一个位置)

(?=.[0-9])表示该位置后面的字符串匹配”.[0-9]”,即有任意多个任意字符,后面再跟个数字,就是接下来的字符,必须包含一个数字。

“至少包含两种字符”的意思就是说,不能全部都是数字,也不能全部都是小写字母,也不能全部都是大写字母。

那么要求”不能全部都是数字”,实现的正则表达式是:

1
regex = r'(?!^[0-9]{6,12}$)^[0-9A-Za-z]{6,12}$'

三种”都不能”的最终实现代码如下:

1
2
3
4
5
6
7
8
9
import re 
regex = r'(?!^[0-9]{6,12}$)(?!^[a-z]{6,12}$)(?![A-Z]{6,12}$)^[0-9A-Za-z]{6,12}$'
pattern = re.compile(regex)
print(not not pattern.match("1234567"))
print(not not pattern.match("abcdef"))
print(not not pattern.match("ABCDEFGH"))
print(not not pattern.match("ab23C"))
print(not not pattern.match("ABCDEF234"))
print(not not pattern.match("abcdEF234"))