一文看懂Python的re模块及正则表达式(regex)

正则表达式(regex)是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。学会使用Python自带的re模块编程非常有用,因为它可以帮我们快速检查一个用户输入的email或电话号码格式是否有效,也可以帮我们快速从文本中提取我们所需要的字符串。今天我们就来看看如何编写python正则表达式, 并利用re模块自带的match, search, findall, sub和split方法来判断字符串的匹配并从目标字符串提取我们想要的内容。

 

re模块的主要方法

Python自带的re模块主要包含如下6种方法,我们稍后会逐一介绍。在使用这些方法之前,我们先要学会如何编写字符串模式(pattern)。

  • re.compile: 编译一个正则表达式模式(pattern)

  • re.match: 从头开始匹配, 使用group()方法可以获取第一个匹配值

  • re.search: 用包含方式匹配,使用group()方法可以获取第一个匹配值

  • re.findall: 用包含方式匹配,把所有匹配到的字符放到以列表中的元素返回多个匹配值

  • re.sub: 匹配字符并替换

  • re.split: 以匹配到的字符当做列表分隔符,返回列表

 

Python正则表达式符号意义

模式描述
^匹配字符串的开头
$匹配字符串的末尾。
.匹配任意字符,除了换行符。
[...]用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k'
[^...]不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。
*匹配0个或多个的表达式。
+匹配1个或多个的表达式。
?匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
{ n}精确匹配 n 个前面表达式。例如, o{2} 能匹配 "food" 中的两个 o。
{ n,}匹配 n 个前面表达式。例如, o{2,} 能匹配 "foooood"中的所有 o。"o{1,}" 等价于 "o+"。"o{0,}" 则等价于 "o*"。
{ n, m}匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
a| b匹配a或b
()匹配括号内的表达式,也表示一个组

 

我们.号匹配所有字符。除此以外我们还可以使用以下字符匹配字母,空白符或数字。如\d代表一位整数,\d+代表一位或多位整数,\d{4}代表四位整数,如年份。

 

 

\w匹配字母数字及下划线
\W匹配非字母数字及下划线
\s匹配任意空白字符,等价于 [\t\n\r\f].
\S匹配任意非空字符
\d匹配任意数字,等价于 [0-9].
\D匹配任意非数字
\A匹配字符串开始
\Z匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。
\z匹配字符串结束

 

re.compile方法

compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。其函数包含两个参数,一个pattern,一个可选参数flags。

re.compile(pattern[, flags])

参数:

  • pattern : 一个字符串形式的正则表达式

  • flags : 可选,表示匹配模式,比如忽略大小写,多行模式等,具体参数为:

    • re.I 忽略大小写

    • re.L 表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境

    • re.M 多行模式

    • re.S 即为 . 并且包括换行符在内的任意字符(. 不包括换行符)

    • re.U 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依赖于 Unicode 字符属性数据库

    • re.X 为了增加可读性,忽略空格和 # 后面的注释

上述flags re.I和re.M是非常常用的。如果要同时使用两个flags,可以使用re.I | re.M。

 

下例中我们编写了一个电子邮箱的正则表达式,并用它来验证用户输入的邮箱是否有效。你可以看到有匹配的对象存在。如果没有匹配到字符串,re.match方法会返回None。因此我们可以用if re.match(pattern, string)来判断来检查是否有匹配。你或许要问了,我们如何从有匹配的字符串中提取匹配到的字符串呢? 我们马上讲到。

>>> import re
>>> email_pattern = re.compile(r'^[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+){0,4}
@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+){0,4}$')
>>> re.match(email_pattern, 'django@pyghon.org')
<_sre.SRE_Match object; span=(0, 17), match='django@pyghon.org'>

 

re.match和re.search方法

re.match和re.search方法类似,唯一不同的是re.match从头匹配,re.search可以从字符串中任一位置匹配。如果有匹配对象match返回,可以使用match.group()提取匹配字符串。

  • re.match(pattern, string)

  • re.search(pattern, string)

 

 

我们来看个实际案例。下例中我们编写了一个年份的正则表达式, 试图用它从"我爱1998和1999年"中提取年份信息。'\d+{4}'代表一个正整数重复4次,即四位整数。你可以看到re.match没有任何匹配,而re.search也只是匹配到1998年,而没有匹配到1999年。这是为什么呢?re.match是从头匹配的,从头没有符合正则表达式,就返回None。re.search方法虽然可以从字符串任何位置开始搜索匹配,但一旦找到第一个匹配对象,其就停止工作了。如果想从一个字符串中提取所需符合正则表达式模式的所有字符串,你需要使用re.findall方法。

>>> year_pattern = re.compile(r'\d{4}$') # 四位整数,匹配年份
>>> string1 = '我爱1998和1999年'
>>> match1 = re.match(year_pattern, string1)
>>> print(match1)
None
>>> match2 = re.search(year_pattern, string1)
>>> print(match2)
<_sre.SRE_Match object; span=(2, 6), match='1998'>
>>> print(match2.group())
1998

 

re.match和re.search方法虽然一次最多只能返回一个匹配对象,但我们可以通过在pattern里加括号构造匹配组返回多个字符串。下例展示了我们如何从"Elephants are bigger than rats"里提取Elephants和bigger两个单词。注意一个括号对于一个group,而match.group的编号是从1开始的,而不是像列表一样从0开始。

>>> string3 = "Elephants are bigger than rats";
>>> match3 = re.search( r'(.*) are (.*?) .*', string3, re.M|re.I)
>>>  print(match3.group())
Elephants are bigger than rats
>>>  print(match3.group(1))
Elephants
>>> print(macth3.group(2))
bigger

 

有的符号如", ', )本身就有特殊的含义,我们在正则表达中使用时必需先对它们进行转义,方法就是在其符号前件反斜杠\。下例展示了我们如何从“总共楼层(共7层)"提取共7层三个字,我们需要给括号转义。我们pattern5和pattern6中都对外面双括号都加了反斜杠\,表明这是括号符号本身。在pattern6中我们还使用了一对没加反斜杠的括号,表明这是一个match group。

>>> string4 = "总共楼层(共7层)"
>>> pattern5 = re.compile(r'\(.*\)')
>>> match5 = re.search(pattern5, string4)
>>> print(match5.group())
(共7层)
>>> pattern6 = re.compile(r'\((.*)\)')
>>> match6 = re.search(pattern6, string4)
>>> print(match6.group())
(共7层)
>>> print(match6.group(1))
共7层

 

那你肯会问了,如果我们有”总共楼层(共7层)干扰)楼层"这样的字符串,加了个干扰问号,那我们该如何匹配(共7层)呢?Python里正则匹配默认是贪婪的,总是尝试匹配尽可能多的字符。非贪婪的则相反,总是尝试匹配尽可能少的字符。如果要使用非贪婪模式,我们需要在., *, ?号后面再加个问好?即可。

>>> string10 = "总共楼层(共7层)干扰)问号"
>>> pattern10 = re.compile(r'\(.*\)') # 默认贪婪模式
>>> pattern11 = re.compile(r'\(.*?\)') # 加问号?变非贪婪模式
>>> print(re.search(pattern10, string10).group())
(共7层)干扰)
>>> print(re.search(pattern11, string10).group())
(共7层)

 

re.findall方法

前面我们已经提到过,当您试图从一个字符串中提取所有符合正则表达式的字符串列表时需要使用re.findall方法。findall方法使用方法有两种,一种是pattern.findall(string) ,另一种是re.findall(pattern, string)。re.findall方法经常用于从爬虫爬来的文本中提取有用信息。

 

# 例1: pattern.findall(string) - 提取年份列表

>>> year_pattern = re.compile(r'\d{4}$') # 四位整数,匹配年份
>>> string1 = '我爱1998和1999年'
year_pattern.findall(string1)
['1998', '1999']

 

# 例2: re.findall(pattern, string) - 提取百度首页带有链接的关键词

import requests
response = requests.get('https://www.baidu.com')
urls = re.findall(r'<a.*>(.*)</a>', response.text,) # 获取带链接的关键词
for url in urls:
    print(url)

 

 

re.sub方法

re.sub的使用方法是re.sub(pattern, new_string, current_string)。下例展示了如何把年份替换为****。该方法经常用于去除空格,无关字符或隐藏敏感字符。

>>> year_pattern = re.compile(r'\d{4}$') # 四位整数,匹配年份
>>> string1 = '我爱1998和1999年'
>>> replaced_str = re.sub(year_pattern, '****', string1)
>>> print(replaced_str)
我爱****和****年

 

re.split方法

re.split的使用方法是re.split(pattern, string),返回分割后的字符串列表。re.split方法并不完美,比如下例中分割后的字符串列表首尾都多了空格,需要手动去除。

>>> string1 = "1cat2dogs3cats4"
>>> import re
>>> list1 = re.split(r'\d+', string1)
>>> print(list1)
['', 'cat', 'dogs', 'cats', '']

 

一文看到Python原创系列其它文章

一文看懂Python面向对象编程(Python学习与新手入门必看)-原创

一文看懂Python对文件和文件夹的操作, 含shutil和glob模板 - 原创

一文看懂Python Web开发常见数据库MangoDB, Memcached和Redis

一文看懂Python字典类型数据常见操作及排序

一文看懂Python多进程与多线程编程(工作学习面试必读)

一文看懂Python及Django不同类型数据的json序列化(面试工作必读)

一文看懂Python列表表达式及高阶函数如lambda, zip, enumerate, map和filter方法

 

大江狗

2018.11.13

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值