Python-正则表达式详解-快速掌握正则表达式核心函数

正则表达式为为高级的文本模式匹配、抽取或文本形式的搜索和替换功能提供了基础。本文主要介绍python正则表达式的一些基础功能,掌握它也可以使得在python编程中处理字符串游刃有余。


1.简介

正则表达式是一些由字符和特殊符号组成的字符串,匹配一系列有相似特征的字符串。Python 通过标准库中的 re 模块来支持正则表达式。

2.特殊符号和字符

表示法

描述

示例

实例解释

literal

匹配文本字符串的字面值

this

匹配this

re1|re2

匹配正则表达式re1 或者 re2

foo|bar

foo或者bar

.

匹配任何字符(除了\n 之外)(一个.一个字符)

b.b

bsb,b1b...

^

匹配字符串起始部分

^Dear

匹配所有Dear开头的字符

$

匹配字符串终止部分

txt$

匹配以txt结尾的字符串

*

匹配0 次或者多次前面出现的正则表达式

5*

0个或多个5

+

匹配1 次或者多次前面出现的正则表达式

5+

1个或多个5

?

匹配0 次或者 1 次前面出现的正则表达式

5?

0个或1个5

{N}

匹配N 次前面出现的正则表达式(精准)

X{3}

3个X

{N,}

匹配N 次前面出现的正则表达式

X{3}

3个及以上X

{M,N}

匹配M~N 次前面出现的正则表达式

X{2,5}

2到5个X

[…]

匹配来自字符集的任意单一字符

[aeiou]

a,e,i,o,u的任一个

[..x−y..]

匹配x~y 范围中的任意单一字符

[2-5], [D-F]

2,3,4,5任一个,D,E,F任一个

[^…]

不匹配此字符集中出现的任何一个字符,包括某一范围的字符

[^aeiou],[^0-9]

不匹配a,e,i,o,u任何一个字母,不匹配任一个数字

表一常见正则表达式符号

表示法

描述

示例

实例解释

\d

匹配任何十进制数字,与[0-9]一致

\d{11}

表示11位数字

\D

非数字字符,与\d 相反

\D{5}

5个非数字字符

\w

匹配任何字母数字字符,与[A-Za-z0-9_]相同

\w+

数字和字符构成的字符串均可匹配到

\W

匹配非数字字母

\s,\n, \t, \r, \v, \f, 等

匹配任何空格字符,匹配一个换行符,匹配一个制表符,匹配回车,匹配垂直制表符,匹配换页符

\S

匹配非空白符号

\b

匹配任何单词边界

er\b

可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'

\B

匹配非单词边界(要包含)

er\B

能匹配"verb" 中的 'er',但不能匹配 "never" 中的 'er'

\A

匹配整个字符串的开始,不支持多行模式

\Aabc

整个字符串abc开头匹配不上xyz\nabc

\Z(大写)

匹配字符串的结束,如果是存在换行,只匹配到换行前的结束字符串。

def\Z

整个字符串匹配以def结尾。匹配不上def\nxyz

\z(小写)

匹配字符串结束

xyz\Z

整个字符串匹配以xyz结尾。可以匹配def\nxyz

\G

匹配最后匹配完成的位置

表二常见正则表达式特殊字符

表示法

描述

示例

实例解释

(?#…)

表示注释,所有内容都被忽略

(?=…)

前向肯定断言

1、表达式一(?<=子组表达式)表达式二

2、表达式一可选,对前向肯定断言没有影响。首先匹配后面的表达式二捕获内容二,然后用子组表达式从开始位置匹配内容二(相当于子组表达式前面固定添加^),如果匹配成功,则断言成功,否则断言失败。断言成功整条表达式返回成功。断言失败整条表达式返回失败。

3、避免没有表达式二

1、re.search(r'(?=abc)abc',"fishc@abcch")

2、re.search(r'@(?=abc)abcch',"fishc@abcch")

3、re.search(r'(?=abc)@abc',"fishc@abcch")

4、re.search(r'fishc@abc(?=abc)ch',"fishc@abcch")

5、re.search(r'(?=.abc)@abc',"fishc@abcch")

6、re.search(r'fishc@(?=abc)',"fishc@abcch")

7、re.search(r'fish(?=abc)',"fishc@abcch")

8、re.search(r'(?=abc)',"fishc@abcch")

1、span=(6, 9), match='abc'

2、span=(5, 11), match='@abcch'

3、None

4、None

5、span=(5, 9), match='@abc'

6、span=(0, 6), match='fishc@'

7、None

8、span=(6, 6), match=''

(?!…)

前向否定断言

1、表达式一(?<=子组表达式)表达式二

2、表达式一可选,对前向否定断言没有影响。首先匹配后面的表达式二捕获内容二,然后用子组表达式从开始位置匹配内容二(相当于子组表达式前面固定添加^),如果匹配成功,则断言失败,否则断言成功。断言成功整条表达式返回成功。断言失败整条表达式返回失败。

3、避免没有表达式二

1、re.search(r'(?!abc)@abc',"fishc@abcch")

2、re.search(r'fishc@abc(?=abc)ch',"fishc@abcch")

3、re.search(r'(?!abc)abc',"fishc@abcch")

4、re.search(r'@(?!abc)abcch',"fishc@abcch")

5、re.search(r'fishc@(?!abc)',"fishc@abcch")

6、re.match(r'fishc@(?!abc)',"fishc@abcch")

7、re.search(r'(?!abc)',"fishc@abcch")

1、span=(5, 9), match='@abc'

2、span=(0, 11), match='fishc@abcch'

3、None

4、None

5、None

6、None

7、span=(0, 0), match=''

(?<=…)

后向肯定断言

1、表达式一(?<=子组表达式)表达式二

2、首先匹配表达式一捕获内容一,然后用子组表达式从结束位置匹配内容一(相当于子组表达式后面固定添加$),如果匹配成功,则断言成功,否则断言失败。断言成功如果有表达式二则执行表达式二,如果没有整条表达式返回成功。如果断言失败,则整条表达式返回失败。

3、避免没有表达式一

1、re.search(r'fishc@abc(?<=abc)',"fishc@abcch")

2、re.search(r'fishc@abcch(?<=abc)',"fishc@abcch"

3、re.search(r'fishc@(?<=abc)abc',"fishc@abcch")

4、re.search(r'fishc@abc(?<=abc)ch',"fishc@abcch"

5、re.search(r'(?<=abc)abc',"fishc@abcch")

6、re.search(r'(?<=abc)ch',"fishc@abcch")

7、re.search(r'(?<=abc)',"fishc@abcch")

1、span=(0, 9), match='fishc@abc'

2、None

3、None

4、span=(0, 11), match='fishc@abcch'

5、None

6、span=(9, 11), match='ch'

7、span=(9, 9), match=''

(?<!…)

后向否定断言

1、表达式一(?<=子组表达式)表达式二

2、首先匹配表达式一捕获内容一,然后用子组表达式从结束位置匹配内容一(相当于子组表达式后面固定添加$),如果匹配成功,则断言失败,否则断言成功。断言成功如果有表达式二则执行表达式二,如果没有整条表达式返回成功。如果断言失败,则整条表达式返回失败。

3、避免没有表达式一

1、re.search(r'fishc@abcch(?<!abc)',"fishc@abcch")

2、re.search(r'fishc@(?<!abc)abc',"fishc@abcch")

3、re.search(r'fishc@abc(?<!abc)',"fishc@abcch")

4、re.search(r'fishc@abc(?<!abc)ch',"fishc@abcch")

5、re.search(r'(?<!abc)abcch',"fishc@abcch")

6、re.search(r'(?<!abc)shc',"fishc@aabcch")

7、re.search(r'(?<!abc)',"fishc@abcch")

1、span=(0, 11), match='fishc@abcch'

2、span=(0, 9), match='fishc@abc'

3、None

4、None

5、span=(6, 11), match='abcch'

6、span=(2, 5), match='shc'

7、span=(0, 0), match=''

表三常见正则表达式扩展表示法

优先级

符号

最高

“\”

“()” “(?:)” “(?=)” “[]”

“*”“+” “?”“{n}” “{n,}” “{n,m}”

“^” “$” “中介字符”

次最低

串接,即相邻字符连接在一起

最低

“|”

表四正则表达式特殊字符的优先级

正则表达式中有些字符具有特殊的含义,如果在匹配中要用到它本来的含义,需要进行转义,使用转义符'\'。而上述表二,可以看出有些字符自带反斜杠,如\d匹配数字。因此我们可以直接理解为: 如果'\'后面跟的字符不是ASCII数字或者ASCII字母,那么正则样式将直接匹配后面跟的字符,例如,.*\$$匹配任何以美元符号结尾的字符串。

3.正则表达式核心函数

Python 当前如何通过使用 re 模块来支持正则表达式,表五列出了来自 re 模块的更多常见函数。下面将介绍常用的函数

函数

描述

compile(pattern,flags = 0)

使用任何可选的标记来编译正则表达式的模式,然后返回一个正则表达式对象

match(pattern,string,flags=0)

尝试使用带有可选的标记的正则表达式的模式来匹配字符串。如果匹配成功,就返回匹配对象;如果失败,就返回None

search(pattern,string,flags=0)

使用可选标记搜索字符串中第一次出现的正则表达式模式。如果匹配成功,则返回匹配对象;如果失败,则返回None

findall(pattern,string [, flags] )

查找字符串中所有(非重复)出现的正则表达式模式,并返回一个匹配列表

finditer(pattern,string [, flags] )

与findall()函数相同,但返回的不是一个列表,而是一个迭代器。对于每一次匹配,迭代器都返回一个匹配对象

split(pattern,string,max=0)

根据正则表达式的模式分隔符,split 函数将字符串分割为列表,然后返回成功匹配的列表,分隔最多操作 max 次(默认分割所有匹配成功的位置)

sub(pattern,repl,string,count=0)

使用repl 替换所有正则表达式的模式在字符串中出现的位置,除非定义 count,否则就将替换所有出现的位置(另见 subn()函数,该函数返回替换操作的数目)

purge()

清除隐式编译的正则表达式模式

group(num=0)

返回整个匹配对象,或者编号为num 的特定子组

groups(default=None)

返回一个包含所有匹配子组的元组(如果没有成功匹配,则返回一个空元组)

groupdict(default=None)

返回一个包含所有匹配的命名子组的字典,所有的子组名称作为字典的键(如果没有成功匹配,则返回一个空字典)

表五常用的函数

3.1 compile()

当我们在Python中使用正则表达式时,re模块内部会干两件事情:①编译正则表达式,如果正则表达式的字符串本身不合法,会报错;②用编译后的正则表达式去匹配字符串。

那么如果一个正则表达式要重复使用几千次,出于效率的考虑,是不是应该先把这个正则先预编译好,接下来重复使用时就不再需要编译这个步骤了,直接匹配,提高效率,compile()就事干这个的,在模式匹配发生之前,正则表达式模式必须编译成正则表达式。

re.compile(pattern,flags )

pattern为一个字符串形式的正则表达式,

flag可选,表示匹配模式,见表六。

re.I、re.IGNORECASE

不区分大小写的匹配

re.L、re.LOCALE

根据所使用的本地语言环境通过\w、\W、\b、\B、\s、\S 实现匹配

re.M、re.MULTILINE

^和$分别匹配目标字符串中行的起始和结尾,而不是严格匹配整个字符串本身的起始和结尾

re.S、rer.DOTALL

“.”(点号)通常匹配除了\n(换行符)之外的所有单个字符;该标记表示

“.”(点号能够匹配全部字符

re.X、re.VERBOSE

通过反斜线转义,否则所有空格加上#(以及在该行中所有后续文字)都被忽略,除非在一个字符类中或者允许注释并且提高可读性

表六常用的模块属性

例子:

import re

m_token=r'\d+'

pattern=re.compile(m_token)

m = pattern.match('12twothree34four')

print(m)

输出结果:

<_sre.SRE_Match object; span=(0, 2), match='12'>

3.2 match()

match() 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match() 就返回 none。

re.match(pattern,string,flags)

pattern 匹配的正则表达式

string 要匹配的字符串。

flags 可选,表示匹配模式,见表六。

例子:

import re

m_token=r'\d+'

pattern=re.compile(m_token)

m1 = pattern.match('12twothree34four')

m2 = pattern.match('abc12twothree34four')

print(m1)

print(m2)

输出结果:

<_sre.SRE_Match object; span=(0, 2), match='12'>

None

说明,只有字符串起始开始匹配成功,才不返回None。

3.3 search()

其实,想要搜索的模式出现在一个字符串中间部分的概率,远大于出现在字符串起始部分的概率。这也就是search()派上用场的时候了。search()的工作方式与 match()完全一致,不同之处在于 search()会用它的字符串参数,在任意位置对给定正则表达式模式搜索第一次出现的匹配情况。如果搜索到成功的匹配,就会返回一个匹配对象;否则,返回 None。

re.search(pattern,string,flags)

pattern 匹配的正则表达式

string 要匹配的字符串。

flags 可选,表示匹配模式,见表六。

例子:

import re

m_token=r'\d+'

pattern=re.compile(m_token)

m1 = pattern.search('12twothree34four')

m2 = pattern.search('abc12twothree34four')

print(m1)

print(m2)

输出结果:

<_sre.SRE_Match object; span=(0, 2), match='12'>

<_sre.SRE_Match object; span=(3, 5), match='12'>

说明:通过比较match(),发现search()只要整个字符串有匹配成功就返回。

3.4 group()和 groups()

成功调用match()或者search()返回的对象为匹配对象,匹配对象有两个主要的方法:group()和groups()。group()方法用于获得一个或多个分组匹配的字符串,要么返回整个匹配对象,要么根据要求返回特定子组。当要获得整个匹配的子串时,可直接使用 group() 或 group(0)。groups()则仅返回一个包含 唯一或者全部子组的元组,等价于(m.group(1), m.group(2), ...)。

例子:

import re

line = "Cats are smarter than dogs"

matchObj1 = re.match(r'(.*) are (.*?) .*', line, re.M | re.I)

matchObj2 = re.match(r'(.*) are (.*?) (.*)', line, re.M | re.I)

print("matchObj1.group() : ", matchObj1.group())

print("matchObj2.group() : ", matchObj2.group())

print("matchObj1.groups() : ", matchObj1.groups())

print("matchObj2.groups() : ", matchObj2.groups())

输出结果:

matchObj1.group() : Cats are smarter than dogs

matchObj2.group() : Cats are smarter than dogs

matchObj1.groups() : ('Cats', 'smarter')

matchObj2.groups() : ('Cats', 'smarter', 'than dogs')

说明:matchObj1和matchObj2的区别在于.*是否为要求返回的特定子组,

因此matchObj1.groups() 只有两个元素 ('Cats', 'smarter'),

matchObj1.groups() 有三个元素 ('Cats', 'smarter', 'than dogs')。

3.5 findall()

findall()在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果有多个匹配模式,则返回元组列表,如果没有找到匹配的,则返回空列表。

match()和 search()的不同之处在于,match 和 search 是匹配一次 findall 匹配所有。

re.findall(string,pos,endpos)

string : 待匹配的字符串。

pos : 可选参数,指定字符串的起始位置,默认为 0。

endpos : 可选参数,指定字符串的结束位置,默认为字符串的长度。

例子:

import re

pattern = re.compile(r'\d+') # 查找数字

result1 = pattern.findall('runoob 123 google 456')

result2 = pattern.findall('run88oob123google456', 0, 10)

print(result1)

print(result2)

输出结果:

['123', '456']

['88', '12']

例子:

import re

result = re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10')

print(result)

输出结果:

[('width', '20'), ('height', '10')]

说明:实例2为多个匹配模式,返回元组列表。

finditer()函数是一个与 findall()函数类似但是更节省内存的变体,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。

例子:

mport re

it = re.finditer(r"\d+","12a32bc43jf3")

print(it)

for a in it:

print(a)

输出结果:

<callable_iterator object at 0x0000021ED9214978>

<_sre.SRE_Match object; span=(0, 2), match='12'>

<_sre.SRE_Match object; span=(3, 5), match='32'>

<_sre.SRE_Match object; span=(7, 9), match='43'>

<_sre.SRE_Match object; span=(11, 12), match='3'>

说明:可以看出,it返回的是一个迭代器,可以提取匹配的内容。

3.6 sub()

sub()是将某字符串中所有匹配正则表达式的部分进行某种形式的替换。

re.sub(replacement, string, count)

replacement是被替换成的文本

string是需要被替换的文本

count是一个可选参数,指最大被替换的数量

例子:

import re

p = re.compile('(blue|white|red)')

print(p.sub('colour','blue socks and red shoes'))

print(p.sub('colour','blue socks and red shoes', count=1))

输出结果:

colour socks and colour shoes

colour socks and red shoes

说明:当添加了count=1只将blue替换成了colour,red没有被替换。

subn()方法执行的效果跟sub()一样,不过它会返回一个二维数组,包括替换后的新的字符串和总共替换的数量。

例子:

import re

p = re.compile('(blue|white|red)')

print(p.subn('colour','blue socks and red shoes'))

print(p.subn('colour','blue socks and red shoes', count=1))

输出结果:

('colour socks and colour shoes', 2)

('colour socks and red shoes', 1)

4.分组

分组就是用一对圆括号“()”括起来的正则表达式,匹配出的内容就表示一个分组。从正则表达式的左边开始看,看到的第一个左括号“(”表示第一个分组,第二个表示第二个分组,依次类推,需要注意的是,有一个隐含的全局分组(就是0),就是整个正则表达式。分完组以后,要想获得某个分组的内容,直接使用group(num)和groups()函数去直接提取就行。

例子:

import re

msg = '<html><h1>hello</h1></html>'

result = re.match(r'<([0-9a-zA-Z]+)><([0-9a-zA-Z]+)>(.+)</([0-9a-zA-Z]+)></([0-9a-zA-Z]+)>$', msg)

#result = re.match(r'<([0-9a-zA-Z]+)>(.+)</\1>$', msg)

print(result)

print(result.group(0))

print(result.group(1))

print(result.group(2))

print(result.group(3))

print(result.group(4))

print(result.group(5))

print(result.groups())

输出结果:

<_sre.SRE_Match object; span=(0, 27), match='<html><h1>hello</h1></html>'>

<html><h1>hello</h1></html>

html

h1

hello

h1

html

('html', 'h1', 'hello', 'h1', 'html')

说明:上述正则表达式中出现了5对(),因此分成了5组,通过groups()中5个元素也可看出。

命名分组:

(?P<name>正则表达式)#name是一个合法的标识符

引用分组:

(?P=name)

注意:P为大写!

例子:

import re

msg = '<html><h1>hello</h1></html>'

result = re.match(r'<(?P<fenzu>[0-9a-zA-Z]+)><([0-9a-zA-Z]+)>(.+)</([0-9a-zA-Z]+)></([0-9a-zA-Z]+)>', msg)

print(result)

print(result.groups())

输出结果:

<_sre.SRE_Match object; span=(0, 27), match='<html><h1>hello</h1></html>'>

('html', 'h1', 'hello', 'h1', 'html')

说明:([0-9a-zA-Z]+)写成(?P<fenzu>[0-9a-zA-Z]+),命名了一个分组fenzu,且仍是5个分组。

例子:

import re

msg = '<html><h1>hello</h1></html>'

result = re.match(r'<(?P<fenzu>[0-9a-zA-Z]+)><([0-9a-zA-Z]+)>(.+)</([0-9a-zA-Z]+)></(?P=fenzu)>', msg)

print(result)

print(result.groups())

输出结果:

<_sre.SRE_Match object; span=(0, 27), match='<html><h1>hello</h1></html>'>

('html', 'h1', 'hello', 'h1')

说明:将后面的([0-9a-zA-Z]+)写成了(?P=fenzu),引用了分组。但是此时,之前的第五个分组'html'因引用了第一个分组,因此只有4个分组。

5.贪婪和非贪婪

“贪婪”,尽可能多的匹配;“非贪婪”,尽可能少的匹配。默认情况下,正则表达式将进行贪婪匹配,加上?为将贪婪匹配模式转为非贪婪匹配模式。

例子:

import re

sentence = """You said "why?" and I say "I don't know"."""

print(re.findall(r'"(.*)"', sentence))

print(re.findall(r'"(.*?)"', sentence))

输出结果:

['why?" and I say "I don\'t know']

['why?', "I don't know"]

说明:贪婪匹配,从sentence中尽可能长的匹配两个””之间的内容,因此打印出整个字符串;加上了?,变成了非贪婪匹配,因此从sentence中尽可能短的匹配两个””之间的内容,匹配到了就输出,因此打印出两个匹配内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值