前言
在记录正则表达式的相关内容之前,贴一个正则表达式的在线验证工具,可以直接在这网站上练习正则表达式
RegExr: 学习、构建 和 测试 正则表达式 Test RegEx
常见语法
普通字符匹配
直接使用想要的字符进行匹配,比如如果你只想在“你好世界”这个字符串中提取“你好”两个字符时,直接使用这两个字符作为匹配模式即可
. :匹配任意一个字符
不同于输入自己想要的字符,. 可以代表任意一个字符,如下图所示,, 可以代表“你好世界”四个字的任意一个字
*:对字符进行任意次数的匹配
*代表可以对字符进行任意次数的匹配,如下图所示,我想要得到文本中的后半段句子,包括,那么我就可以使用表达式 ,.*
其中,表示匹配字符, .表示匹配,后边的任意一个字符,*表示匹配任意一个字符任意次
+:对字符进行一次或者多次匹配
不同于*,+表示对字符匹配至少一次,不能包含0次,而*可以匹配0次,也就是字符可以不出现
在该例子中,我们将上述的正则模式更换为 ,.+
可以看到我们只匹配到了,后边有字符的部分,而对于没有字符的部分无法获取到,因为+不能匹配0次
?:对字符进行匹配0次或者一次
?表示对字符匹配0次或者1次,同样见下例
{}:自定义字符匹配次数
自定义指定对字符的匹配次数,见下例,只对,后的字符出现两次的进行提取
当然也可以多写几个任意次数
[]:模糊匹配
含义:表示要匹配的几个指定字符之一。
[abc]
可以匹配a、b、c
中任意一个字符,等价于:[a-c]
如下,匹配字符串中的字符“的”,“式”,“呀”中的任意一个,如果匹配到就返回,匹配不到不返回
中括号表达式的其他用法还有一个其他用法,该用法与模糊匹配的含义完全不同,不要混淆
即,如果在中括号中使用^
则表示非,不匹配在中括号中的字符集合。
import re
content = 'a1b2c3d4e5'
for temp in re.findall(r'[^\d]', content):
print(temp,end=" ")
上述表达式中表示匹配字符串中,不是数字的所有字符,结果如下
():分组匹配
分组匹配常用于在字符串中提取我们想要的部分内容,如下所示,如果我们只想得到字符串中的水果名字,而不想得到其后的中文, 使用分组即可得到结果
该正则表达式表示,提取出以中文,结尾的()中的若干字符
再如以下示例,我想得到字符串中的人物姓名及其对应的手机号码,我们也可以使用如下正则表达式进行提取,其含义为:
(.+),
: 匹配任意字符(除换行符外)一次或多次,并将匹配的结果保存到一个分组中,然后匹配一个逗号。.+
: 匹配任意字符(除换行符外)一次或多次。(.{11})
: 匹配任意字符(除换行符外)恰好11次,并将匹配的结果保存到一个分组中。
贪婪模式与非贪婪模式
假如现在我们有一个重复的标签内容:
aa<div>test1</div>bb<div>test2</div>cc
假如我们现在想得到div标签,该如何得到呢?
贪婪模式
如果使用以下语句
source = 'aa<div>test1</div>bb<div>test2</div>cc'
for temp in re.findall(r'<div>.*</div>', source):
print(temp)
最终得到的结果是:
<div>test1</div>bb<div>test2</div>
非贪婪模式
而如果使用以下语句:
import re
source = 'aa<div>test1</div>bb<div>test2</div>cc'
for temp in re.findall(r'<div>.*?</div>', source):
print(temp)
最终得到的结果是
<div>test1</div>
<div>test2</div>
很显然,第二种结果才是我们想要的
可以这样认为,贪婪模式,就是在整个表达式匹配成功的前提下,尽可能多的匹配,也就是所谓的“贪婪”,通俗点讲,就是看到想要的,有多少就捡多少,除非再也没有想要的了。
非贪婪模式,就是在匹配到第一个“</div>”时使整个表达式匹配成功时就结束匹配,不再向右尝试,也就是在整个表达式匹配成功的前提下,尽可能少的匹配,匹配结果为“<div>test1</div>”。
字符转义
反斜杠\
在正则表达式中有多重用途,例如在以下文本中搜索所有.
之前的字符串,也包含.
本身。
苹果.是绿色的 橙子.是橙色的 香蕉.是黄色的
如果我们将正则表达式写成:.*.
,肯定是不正确的,因为.
是一个元字符,具有特殊含义。直接出现在正则表达式中不能表示.
这个字符本身。
解决方式:使用\
转义。
import re
content = '''
苹果.是绿色的
橙子.是橙色的
香蕉.是黄色的
'''
for temp in re.findall(r'.*\.', content):
print(temp)
某种字符类型的匹配
在反斜杠后链接一些字符可以表示某种类型的一个字符,如下:
含义 | 等价表达式 | |
\d | 匹配0-9之间任意一个数字字符 | [0-9] |
\D | 匹配任意一个不是0-9之间的数字字符 | [^0-9] |
\s | 匹配任意一个空白字符,包括空格、tab 、换行符等 | [\t\n\r\f\v] |
\S | 匹配任意一个非空白字符 | [^\t\n\r\f\v] |
\w | 匹配任意一个文字字符,包括大小写字母、数字、下划线 | [a-zA-Z0-9_] |
\W | 匹配任意一个非文字字符 | [^a-zA-Z0-9_] |
首尾匹配与单行、多行模式
^
表示匹配文本的开头位置。在正则表达式中可以设置单行模式与多行模式。
-
单行模式:表示匹配整个文本的开头位置
-
多行模式:表示匹配文本每行的开头位置
在下面的文本中,每行最前面的数字表示水果的编号,最后的数字表示价格。
001-苹果价格-60
002-橙子价格-70
003-香蕉价格-80
如果我们要提取所有的水果编号,用这样的正则表达式:^\d+
import re
content = '''001-苹果价格-60
002-橙子价格-70
003-香蕉价格-80
'''
res=re.findall(r'^\d+',content,re.M)
print(res)
结果如下:
上述表达式的含义是:取出以任意数字开头的若干个数字字符
参数re.M表示匹配多行,如果没有这个参数,则只能匹配到001
同样的道理,如果我们想得到水果的价格,则可以使用这样的表达式:\d+$
import re
content = '''001-苹果价格-60
002-橙子价格-70
003-香蕉价格-80
'''
res=re.findall(r'\d+$',content,re.M)
print(res)
结果如下:
DOTALL
参数
DOTALL
标记允许点号匹配所有字符,包括换行符。这对于需要处理包含换行符的文本时非常有用,因为默认情况下点号无法匹配换行符。
在以下html
代码中提取所有的职位名称:
<div class="el">
<p class="t1">
<span>
<a>Python开发工程师</a>
</span>
</p>
<span class="t2">南京</span>
<span class="t3">1.5-2万/月</span>
</div>
<div class="el">
<p class="t1">
<span>
<a>java开发工程师</a>
</span>
</p>
<span class="t2">苏州</span>
<span class="t3">1.5-2/月</span>
</div>
如果直接使用表达式class=\"t1\">.*?<a>(.*?)</a>
会发现匹配不上,因为t1
和<a>
之间有两个空行,这时就需要.
匹配所有字符了。
import re
content = '''
<div class="el">
<p class="t1">
<span>
<a>Python开发工程师</a>
</span>
</p>
<span class="t2">南京</span>
<span class="t3">1.5-2万/月</span>
</div>
<div class="el">
<p class="t1">
<span>
<a>java开发工程师</a>
</span>
</p>
<span class="t2">苏州</span>
<span class="t3">1.5-2/月</span>
</div>
'''
for temp in re.findall(r'class=\"t1\">.*?<a>(.*?)</a>', content, re.DOTALL):
print(temp)