正则表达式

常用的匹配规则

匹配规则

描述

示例

.

匹配除换行符以外的任何单个字符

a.b 匹配 aab, a0b, a*b

^

匹配字符串的开始

^abc 匹配 abcdef 中的 abc

$

匹配字符串的结尾

abc$ 匹配 123abc 中的 abc

*

匹配前面的字符 0 次或多次

ab*c 匹配 ac, abc, abbc

+

匹配前面的字符 1 次或多次

ab+c 匹配 abc, abbc

?

匹配前面的字符 0 次或 1 次

ab?c 匹配 ac, abc

{n}

匹配前面的字符恰好 n 次

a{3} 匹配 aaa

{n,}

匹配前面的字符至少 n 次

a{3,} 匹配 aaa, aaaa, aaaaa

{n,m}

匹配前面的字符至少 n 次,至多 m 次

a{3,5} 匹配 aaa, aaaa, aaaaa

[abc]

匹配方括号内的任意一个字符

[abc] 匹配 a, b, c

[a-z]

匹配小写字母范围内的任意一个字符

[a-z] 匹配 a 到 z

[A-Z]

匹配大写字母范围内的任意一个字符

[A-Z] 匹配 A 到 Z

[0-9]

匹配数字范围内的任意一个字符

[0-9] 匹配 0 到 9

[^abc]

匹配不在方括号内的任意一个字符

[^abc] 匹配 d, e, f 等

\d

匹配任何数字,相当于 [0-9]

\d 匹配 0 到 9

\D

匹配任何非数字字符,相当于 [^0-9]

\D 匹配 a, b, c 等

\w

匹配任何字母数字字符,相当于 [a-zA-Z0-9_]

\w 匹配 a 到 z, A 到 Z, 0 到 9 和 _

\W

匹配任何非字母数字字符,相当于 [^a-zA-Z0-9_]

\W 匹配 @, #, 空格 等

\s

匹配任何空白字符,相当于 [ \t\n\r\f\v]

\s 匹配 空格, \t, \n 等

\S

匹配任何非空白字符,相当于 [^ \t\n\r\f\v]

\S 匹配 a, 1, @ 等

\b

匹配一个单词边界

\bword\b 匹配 word

\B

匹配非单词边界

\Bword\B 匹配 password 中的 word

(...)

匹配括号内的表达式,并捕获该匹配的子字符串

(abc) 匹配并捕获 abc

(?:...)

匹配括号内的表达式,但不捕获该匹配的子字符串

(?:abc) 匹配 abc 但不捕获

(?P<name>...)

匹配括号内的表达式,并捕获该匹配的子字符串,赋予组名 name

(?P<word>\w+) 捕获一个单词,并命名为 word

\num

引用编号为 num 的捕获组

\1 引用第一个捕获组

(?P=name)

引用名称为 name 的捕获组

(?P=word) 引用名为 word 的捕获组

*?

匹配前面的字符 0 次或多次,非贪婪

.*? 匹配最少字符

+?

匹配前面的字符 1 次或多次,非贪婪

.+? 匹配最少字符

??

匹配前面的字符 0 次或 1 次,非贪婪

a?? 在 a 中匹配 a

{n,m}?

匹配前面的字符至少 n 次,至多 m 次,非贪婪

a{2,5}? 在 aaaaaa 中匹配 aa

(?=...)

正向肯定断言,匹配当前位置后面的内容

\d(?=px) 匹配 3px 中的 3

(?!...)

正向否定断言,匹配当前位置后面不出现的内容

\d(?!px) 匹配 3em 中的 3

(?<=...)

反向肯定断言,匹配当前位置前面的内容

(?<=\$)\d+ 匹配 $100 中的 100

(?<!...)

反向否定断言,匹配当前位置前面不出现的内容

(?<!\$)\d+ 匹配 100 中的 100

match

常用的匹配方法,传入要匹配的字符串以及正则表达式,可以检测这个正则表达式和字符串是否相匹配

会尝试从字符串的起始位置开始匹配正则表达式,如果匹配返回匹配成功的结果,若不匹配返回None

import re

content = 'Hello 123 4567 World_This is a Regex Demo'
print(len(content))
# ^匹配一行字符串的开头
# \s匹配任意空白字符
# \d匹配任意数字
# \w匹配字母数字加下划线
result = re.match('^Hello\s\d\d\d\s\d{4}\s\w{10}', content)
print(result)
print(result.group())
print(result.span())

 匹配目标

若想字符串中提取一部分内容,可以使用()将想提取的子字符串括起来。()实际上标记了一个子表达式的开头和结束位置,被标记的每个子表达式一次对应每个分组,调用group方法传入分组的索引即可获取提取的结果。

import re

content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^Hello\s(\d+)\sWorld', content)
print(result)
print(result.group())
print(result.group(1))
print(result.span())

若出现AttributeError: 'NoneType' object has no attribute 'group'
表示没有匹配到符合正则表达式的内容,但又调用了group方法。

通用匹配

.可以匹配任意字符(除换行符),*代表匹配前面的字符无限次,组合在一起就可以匹配任意字符。

import re

content = 'Hello 123 4567 World_This is a Regex Demo'
result = re.match('^Hello.*Demo$', content)
print(result)
print(result.group())
print(result.span())

 贪婪与非贪婪

贪婪匹配

import re

content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^He.*(\d+).*Demo$', content)
print(result)
print(result.group(1))

 在贪婪模式下,.*会匹配尽可能多的字符。正则表达式中.*后面是(\d+),也就是至少一个数字,而且没有指定具体几个数字,所以第一个.*会匹配尽可能多的字符,把123456全都匹配了,只给\d+留下一个7

非贪婪匹配

import re

content = 'Hello 1234567 World_This is a Regex Demo'
result = re.match('^He.*?(\d+).*Demo$', content)
print(result)
print(result.group(1))

 .*?非贪婪匹配匹配尽量少的字符,当 .*?匹配打后面的空白字符再往后及时数字,而\d+恰好可以匹配,所以结果为1234567

import re

content = 'http://weibo.com/comment/sdJHJfh'
result1 = re.match('http.*?comment/(.*?)', content)
result2 = re.match('http.*?comment/(.*)', content)
print('result1', result1.group(1))
print('result2', result2.group(1))

 在做匹配的时候,字符串中间尽量使用非贪婪匹配以避免出现匹配结果却是的情况。

如果匹配的解雇哦在字符串结尾,.*?有可能匹配不到任何内容

修饰符

在正则表达式中可以用一些标志修饰符来控制匹配的模式。

 正则表达式没有匹配到这个字符返回AttributeError: 'NoneType' object has no attribute 'group'

因为匹配的内容是除换行符之外的任意字符,当遇到换行符时,.*?就不能匹配了,所以导致匹配失败。

这里加一个修饰符re.S即可修饰这个错误

result = re.match('^He.*?(\d+).*?Demo$', content, re.S)

常用修饰符列表

修饰符完整名称解释
re.IGNORECASE re.I 使匹配对大小写不敏感
re.MULTILINE re.M 使开始和结束字符(^ 和 $)分别匹配每一行的开始和结束,而不是整个字符串的开始和结束
re.DOTALL re.S 使点号(.)匹配任何字符,包括换行符
re.VERBOSE re.X 允许正则表达式包含空白和注释,以提高可读性
re.ASCII re.A 使 \w\W\b\B\s\S\d\D 等只匹配ASCII字符,而不是Unicode字符(Python 3.7+ 中移除)
re.UNICODE re.U 使 \w\W\b\B\s\S\d\D 等匹配Unicode字符(在Python 3.3+ 中是默认行为,Python 3.7+ 中移除)
re.LOCALE re.L 使预定义字符类(如 \w\W\b\B 等)的行为取决于当前区域设置(不推荐使用,因为这会使得正则表达式的行为依赖于环境)

转义匹配

import re

content = '(百度)www.baidu.com'
result = re.match('\(百度\)www\.baidu\.com', content)
print(result)

 search

match方法从字符串的开头开始匹配,一旦不从开头匹配,匹配就会失效。

import re

content = 'Extra strings Hello 1234567 World_This is a Regex Demo Extra     stings'
result = re.match('Hello.*?(\d+).*?Deom', content)
print(result)

 

 正则表达式以Hello开头,字符串以Extra开头,所以匹配失败。

import re

content = 'Extra strings Hello 1234567 World_This is a Regex Demo Extra     stings'
result = re.search('Hello.*?(\d+).*?Demo', content)
print(result)

 使用search扫整个字符串,返回第一个匹配成功的结果。

这是一段待匹配HTML开头

html = '''<div id="songs-list">
<h2 class="title"> 经典老歌 </h2>
<p class="introduction">
经典老歌列表
</p>
<ul id="list" class="list-group">
<li data-view="2"> 一路上有你 </li>
<li data-view="7">
<a href="/2.mp3" singer="任贤齐"> 沧海一声笑 </a>
</li>
<li data-view="4" class="active">
<a href="/3.mp3" singer="齐秦"> 往事随风 </a>
</li>
<li data-view="6"><a href="/4.mp3" singer="beyond"> 光辉岁月 </a></li>
<li data-view="5"><a href="/5.mp3" singer="陈慧琳"> 记事本 </a></li>
<li data-view="5">
<a href="/6.mp3" singer="邓丽君"> 但愿人长久 </a>
</li>
</ul>

尝试提取第三个li里面的歌手名和歌名,正则表达式写法:

<li.*?active.*?singer="(.*?)">(.*?)</a>

若正则表达式不带active则会匹配第一个符合条件的目标,从字符串开头开始搜索,此时符合条件的节点就变成了第二个li节点,后面的就不在匹配

result = re.search('<li.*?singer="(.*?)">(.*?)</a>', html, re.S)
if result:
    print(result.group(1), result.group(2))

 在上面的两次匹配中search方法的第三个参数都添加了re.S,这使得.*?可以匹配换行,如果去掉则会匹配

不包含换行符的第四个li

 findall

findall可以获取与正则表达式相匹配的所有字符串

results = re.findall('<li.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>', html, re.S)
print(results)

 sub

除了使用正则表达式提取信息外,有时候还需要借助它来修改文本。比如,想要把一串文本中的所有数字都去掉,如果只用字符串的 replace 方法太烦琐,可以借助 sub 方法

import re

content = 'a5s4d531fc3a5sd4sa35d4'
content = re.sub('\d+', '', content)
print(content)

 第一个参数传入 \d+ 来匹配所有的数字,第二个参数为替换成的字符串(如果去掉该参数的话,可以赋值为空),第三个参数是原字符串。

上面的HTML文本中,获取所有li节点的歌名,可以如以下写法:

# 用 sub 方法将 a 节点去掉,只留下文本,然后再利用 findall 提取
html = re.sub('<a.*?>|</a>', '', html)
print(html)
results = re.findall('<li.*?>(.*?)</li>', html, re.S)
for result in results:
    print(result.strip())

 compile

可以将正则字符串编译成表达式对象,以便在后面的匹配中使用。 3 个日期,分别将 3 个日期中的时间去掉,可以借助 sub 方法。第一个参数是正则表达式,这里没有必要重复写 3 个同样的正则表达式,可以借助 compile 方法将正则表达式编译成一个正则表达式对象,以便以后使用

import re

content1 = '2024-1-28 23:23'
content2 = '2024-1-22 23:21'
content3 = '2024-1-23 23:25'
pattern = re.compile('\d{2}:\d{2}')
result1 = re.sub(pattern, '', content1)
result2 = re.sub(pattern, '', content2)
result3 = re.sub(pattern, '', content3)
print(result1, result2, result3)

 compile 还可以传入修饰符,例如 re.S 等修饰符,这样在 search、findall 等方法中就不需要额外传了。所以,compile 方法可以说是给正则表达式做了一层封装,以便更好地复用。

posted @ 2024-05-24 01:44  JJJhr  阅读(9)  评论(0编辑  收藏  举报