Python 正则表达式

正则表达式

1. 正则表达式概述

  • 正则表达式,又称正规表示式、正规表示法、正规表达式、规则表达式、常规表示法(英语:Regular Expression,在代码中常简写为regex、regexp或RE),是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些匹配某个模式的文本。
  • Regular Expression的“Regular”一般被译为“正则”、“正规”、“常规”。此处的“Regular”即是“规则”、“规律”的意思,Regular Expression即“描述某种规则的表达式”之意。

2. 模块操作

在Python中需要通过正则表达式对字符串进行匹配的时候,可以使用一个模块,名字为re
2.1 re模块的使用过程

  # 导入re模块
import re

# 使用match方法进行匹配操作
result = re.match(正则表达式,要匹配的字符串)

# 如果上一步匹配到数据的话,可以使用group方法来提取数据
result.group()
  • **re.match **

查询标志

大部分查询方法还可以接受一个查询标志参数。查询标志让正则表达式具有不同的行为。下面一一说明。

标志 作用
re.A、
re.ASCII
以ASCII模式查询,默认是Unicode模式
re.DEBUG 显示编译表达式的调试信息
re.I、re.IGNORECASE 忽略字母的大小写
re.L、re.LOCALE 以区域敏感方式查询匹配
re.M、re.MULTILINE 开启多行模式,开启之后行边界符^$会匹配每行的开始和结束,而不是整个字符串的开始和结束
re.S、re.DOTALL 使用此标志,会让点符号匹配所有字符,默认情况下点符号会匹配换行符以外的符号
re.X、re.VERBOSE 开启啰嗦模式,可以在写正则表达式的时候添加注释

re.match是用来进行正则匹配检查的方法,若字符串匹配正则表达式,则match方法返回匹配对象(Match Object),否则返回None(注意不是空字符串"")。
匹配对象Macth Object具有group方法,用来返回字符串的匹配部分。

2.2re模块示例(匹配以taobao开头的语句)

>>>import re
>>>re.match('taobao', 'taobao.com')
<_sre.SRE_Match object; span=(0, 6), match='taobao'>
>>>re.match('taobao', 'taobao.com').group()
'taobao'

说明

  • re.match() 能够匹配出以xxx开头的字符串,是从第一个字符开始匹配的

3. 表示字符

字符 功能
中文 [\u4e00-\u9fa5]
. 匹配任意一个字符(除了\n)
[ ] 匹配[ ]中列举的一个字符
\d 匹配数字,即0-9
\D 匹配非数字,即不是数字
\s 匹配空白,即空格,tab键
\S 匹配非空白
\w 匹配单词字符,即a-z、A-Z、0-9、_,还有各个国家的文字
\W 匹配非单词字符
  • .匹配任意一个字符(除了\n)
>>>import re
>>>re.match('.','abc')
<_sre.SRE_Match object; span=(0, 1), match='a'>
>>>re.match('.','abc').group()
'a'
>>>re.match('.','\nabc')
>>>re.match('.','\tabc')
<_sre.SRE_Match object; span=(0, 1), match='\t'>
>>>re.match('.','\tabc').group()
'\t'

匹配[ ]中列举的一个字符

>>>import re
>>>re.match("[hH]","hello Python")
<_sre.SRE_Match object; span=(0, 1), match='h'>
>>>re.match("[hH]","Hello Python")
<_sre.SRE_Match object; span=(0, 1), match='H'>
>>>re.match("[0-9]","7Hello Python")
<_sre.SRE_Match object; span=(0, 1), match='7'>

\w 匹配单词字符,即a-z、A-Z、0-9、_,还有各个国家的文字。加上第三个参数re.ASCII就是以ASCII码匹配,就只能匹配字母、数字、下划线

>>>import re
>>>re.match('\w','かきく')
<_sre.SRE_Match object; span=(0, 1), match='か'>
>>>re.match('\w','ㅈㅊㅅ')
<_sre.SRE_Match object; span=(0, 1), match='ㅈ'>
>>>re.match('\w','汉字')
<_sre.SRE_Match object; span=(0, 1), match='汉'>
>>>re.match('\w','abc')
<_sre.SRE_Match object; span=(0, 1), match='a'>
>>>re.match('\w','汉字',re.ASCII)#这个就没有匹配成功
>>>re.match('\w','a汉字',re.ASCII)
<_sre.SRE_Match object; span=(0, 1), match='a'>

4. 原始字符串

在很多编程语言中,由于有转义字符这么一种东西的存在,导致正则表达式需要使用两个斜杠来处理。

  • Python中字符串前面加上 r 表示原生字符串,
  • 与大多数编程语言相同,正则表达式里使用""作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符"",那么使用编程语言表示的正则表达式里将需要4个反斜杠 "\" :前 两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。
  • Python里的原生字符串很好地解决了这个问题,有了原始字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
>>>import re
>>>m = "c:\\a\\b\\c"
>>>m
'c:\\a\\b\\c'
>>>print(m)
c:\a\b\c
>>>re.match("c:\\\\",m).group()
'c:\\'
>>>print(re.match("c:\\\\",m).group())
c:\
>>>re.match(r"c:\\a",m).group()
'c:\\a'
>>>print(re.match(r"c:\\a",m).group())
c:\a

5. 表示数量

匹配多个字符的相关格式

字符 功能
* 匹配前面的子表达式任意次,即可有可无
+ 匹配前一个字符出现1次或者无限次,即最少有一次
? 匹配前一个字符出现1次或者0次,即要么有1次,要么没有
{m} 匹配前一个字符出现m次
{m,} 匹配前一个字符至少出现m 次
{m,n} 匹配前一个字符出现从m到n次

示例1 *
匹配出,一个字符串第一个字母为大小字符,后面都是小写字母并且这些小写字母可有可无

>>>import re
>>>re.match("[A-Z][a-z]*","Aabcdef")
<_sre.SRE_Match object; span=(0, 7), match='Aabcdef'>

示例2:?
需求:匹配出,0到99之间的数字

>>>import re
re.match("[1-9]?[0-9]","33")
>>><_sre.SRE_Match object; span=(0, 2), match='33'>
>>>re.match("[1-9]?[0-9]","09")
<_sre.SRE_Match object; span=(0, 1), match='0'>
>>>re.match("[1-9]?[0-9]","9")
<_sre.SRE_Match object; span=(0, 1), match='9'>

示例3:{m,n}
需求:匹配出,8到20位的密码,可以是大小写英文字母、数字、下划线

>>>import re
>>>re.match("[a-zA-Z0-9_]{8,20}","1ad12f23s34455ff66")
<_sre.SRE_Match object; span=(0, 18), match='1ad12f23s34455ff66'>

示例4:
匹配出163的邮箱地址,且@符号之前有4到20位,例如hello@163.com

import re
print('6~18个字符,可使用字母、数字、下划线,需以字母开头')
while True:
    address=input('>>>输入邮箱地址:')
    ret=re.match('[a-zA-Z]\w{3,19}@163\.com',address)
    if ret == None:
        print('输入有误,请重新输入。邮箱格式为6~18个字符,可使用字母、数字、下划线,需以字母开头')
        continue
    else:
        ret = ret.group()
        print('您的邮箱地址:%s'%ret)
        break

运行结果

G:\tools\python3.5\python.exe E:/workspace/day29/练习1.py
6~18个字符,可使用字母、数字、下划线,需以字母开头
>>>输入邮箱地址:haha
输入有误,请重新输入。邮箱格式为6~18个字符,可使用字母、数字、下划线,需以字母开头
>>>输入邮箱地址:haha@163.com
您的邮箱地址:haha@163.com

6. 表示边界

字符 功能
^ 匹配字符串的开头
$ 匹配字符串的末尾
\b 匹配一个单词的边界
\B 匹配非单词边界

示例 $

>>>import re
>>>re.match("[\w]{4,20}@163\.com", "xiaoWang@163.comheihei")
<_sre.SRE_Match object; span=(0, 16), match='xiaoWang@163.com'>
>>>re.match("[\w]{4,20}@163\.com$", "xiaoWang@163.comheihei")
>>>re.match("[\w]{4,20}@163\.com", "xiaoWang@163.comheihei")
<_sre.SRE_Match object; span=(0, 16), match='xiaoWang@163.com'>

7. 匹配分组

字符 功能
| 匹配左右任意一个表达式
(ab) 将括号中字符作为一个分组
\num 引用分组num匹配到的字符串
(?P<name>) 分组起别名
(?P=name) 引用别名为name分组匹配到的字符串

示例 |

>>>import re
>>>re.match("\w{4,20}@(163|126|qq)\.com", "test@gmail.com")
>>>re.match("\w{4,20}@(163|126|qq)\.com", "test@qq.com")
<_sre.SRE_Match object; span=(0, 11), match='test@qq.com'>
>>>re.match("\w{4,20}@(163|126|qq)\.com", "test@qq.com").group(1)
'qq'

示例 \number

>>>import re
>>>re.match(r"<(\w*)><(\w*)>.*</\2></\1>", "<html><h1>www.taobao.com</h1></html>")
<_sre.SRE_Match object; span=(0, 36), match='<html><h1>www.taobao.com</h1></html>'>
>>>re.match(r"<(\w*)><(\w*)>.*</\2></\1>", "<html><h1>www.taobao.com</h2></html>")

第二个匹配不到是后面的\2引用了分组2的值(h1),与字符串中的‘h2’不匹配,所以返回None

示例:(?P<name>) (?P=name)

>>>import re
>>>re.match(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.taobao.com</h1></html>")
<_sre.SRE_Match object; span=(0, 36), match='<html><h1>www.taobao.com</h1></html>'>
>>>re.match(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>", "<html><h1>www.taobao.com</h2></html>")

注意:
(?P<name>)和(?P=name)中的字母p大写

8. re模块的高级用法

1.search()
我们在前面提到过match()方法是从字符串的开头开始匹配,一旦开头不匹配,那么整个匹配就失败了。

import re

s='Hello World 123abc456'
result=re.match('World.*abc',s)
print(result)
result=re.search('World.*abc',s)
print(result)

运行结果

None
<_sre.SRE_Match object; span=(6, 18), match='World 123abc'>

在这里我们有一个字符串,它是以Hello开头的,但是正则表达式我们是以World开头的,整个正则表达式是字符串的一部分,但是这样匹配是失败的,也就是说只要第一个字符不匹配整个匹配就不能成功。

所以match()方法在我们在使用的时候需要考虑到开头的内容,所以在做匹配的时候并不那么方便,它适合来检测某个字符串是否符合某个正则表达式的规则。
 
 在这里就有另外一个方法search(),它在匹配时会扫描整个字符串,然后返回第一个成功匹配的结果,也就是说,正则表达式可以是字符串的一部分,在匹配时,search()方法会依次扫描字符串,直到找到第一个符合规则的字符串,然后返回匹配内容,如果搜索完了还没有找到,那就返回None。

我们把上面的代码中的match()方法修改成search(),这样就得到了匹配结果。所以说,为了匹配方便,我们可以尽量使用search()方法。

2.findall()
  在前面我们说了search()方法的用法,它可以返回匹配正则表达式的第一个内容,但是如果我们想要获取匹配正则表达式的所有内容的话怎么办?这时就需要借助于findall()方法了。

findall()方法会搜索整个字符串然后返回匹配正则表达式的所有内容。

3.sub()
正则表达式除了提取信息,我们有时候还需要借助于它来修改文本,比如我们想要把一串文本中的所有数字都去掉,如果我们只用字符串的replace()方法那就太繁琐了,在这里我们就可以借助于sub()方法。

示例
将匹配到的数字加1

import re

ret = re.sub(r"\d+", '998', "python = 997")
print(ret)
import re


def add(temp):
    strNum = temp.group()
    num = int(strNum) + 1
    return str(num)


ret = re.sub(r"\d+", add, "python = 997")
print(ret)

ret = re.sub(r"\d+", add, "python = 99")
print(ret)

运行结果

python = 998
python = 100

可以传一个函数进去

4.split()
根据匹配进行切割字符串,并返回一个列表
切割字符串“info:xiaoZhang 33 shandong”

>>>import re
>>>re.split(r":| ","info:xiaoZhang 33 shandong")
['info', 'xiaoZhang', '33', 'shandong']

5.compile()
  前面我们所讲的方法都是用来处理字符串的方法,最后再介绍一个compile()方法,这个方法可以讲正则字符串编译成正则表达式对象,以便于在后面的匹配中复用。
例如这里有三个日期,我们想分别将三个日期中的时间去掉,所以在这里我们可以借助于sub()方法,sub()方法的第一个参数是正则表达式,但是这里我们没有必要重复写三个同样的正则表达式,所以可以借助于compile()函数将正则表达式编译成一个正则表达式对象,以便复用。
示例

import re
time1='2017-6-15 11:30'
time2='2017-6-16 12:30'
time3='2017-6-17 11:30'
pattern = re.compile('\d{2}:\d{2}')
result1 = re.sub(pattern,'',time1)
result2 = re.sub(pattern,'',time2)
result3 = re.sub(pattern,'',time3)
print(result1)
print(result2)
print(result3)

运行结果

2017-6-15 
2017-6-16 
2017-6-17 

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

9. 贪婪和非贪婪

Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;
非贪婪则相反,总是尝试匹配尽可能少的字符。
"*","?","+","{m,n}"后面加上,使贪婪变成非贪婪。
练习代码

import re
s = "This is a number 234-235-22-423"
r1 = re.match("(.+)(\d+-\d+-\d+-\d+)", s)
print('r1...')
print(r1.groups())

r2 = re.match("(.+?)(.*?)(\d+-\d+-\d+-\d+?)", s)
print('r2...')
print(r2.groups())

r3 = re.match("(.+)(.+?)(\d+-\d+-\d+-\d+?)", s)
print('r3...')
print(r3.groups())

r4 = re.match("(.+)(.*?)(\d+?)(-\d+-\d+-\d+?)", s)
print('r4...')
print(r4.groups())
r5 = re.match("(.+?)(.*)(\d+?)(-\d+-\d+-\d+?)", s)
print('r5...')
print(r5.groups())

分析一波代码
r1(.+) + 匹配前一个字符出现1次或者无限次,即最少有一次 从第一个开始匹配,并贪婪的尽可能多的匹配,将23也匹配上,给下一个(\d+)留一个4,因为(\d+)至少匹配一个
r2 (.+?)从第一个开始匹配,并 非贪婪的匹配到一个T(.*?)接着上个表达式继续匹配,匹配到2的时候发现(\d+-\d+-\d+-\d+?)符合匹配,就结束匹配得到自己的his is a number
r4 (.+)贪婪的匹配,发现下一个正则表达式(.*)可有可无,于是就接着匹配到(\d+?) 然后(.*)这个组就是空。
运行结果

r1...
('This is a number 23', '4-235-22-423')
r2...
('T', 'his is a number ', '234-235-22-4')
r3...
('This is a number 2', '3', '4-235-22-4')
r4...
('This is a number 23', '', '4', '-235-22-4')
r5...
('T', 'his is a number 23', '4', '-235-22-4')

10. 练习

一、从下面的字符串中取出文本(将标签去掉)

import re
s= '''<div>
        <p>岗位职责:</p>
<p>完成推荐算法、数据统计、接口、后台等服务器端相关工作</p>
<p><br></p>
<p>必备要求:</p>
<p>良好的自我驱动力和职业素养,工作积极主动、结果导向</p>
<p>&nbsp;<br></p>
<p>技术要求:</p>
<p>1、一年以上 Python 开发经验,掌握面向对象分析和设计,了解设计模式</p>
<p>2、掌握HTTP协议,熟悉MVC、MVVM等概念以及相关WEB开发框架</p>
<p>3、掌握关系数据库开发设计,掌握 SQL,熟练使用 MySQL/PostgreSQL 中的一种<br></p>
<p>4、掌握NoSQL、MQ,熟练使用对应技术解决方案</p>
<p>5、熟悉 Javascript/CSS/HTML5,JQuery、React、Vue.js</p>
<p>&nbsp;<br></p>
<p>加分项:</p>
<p>大数据,数理统计,机器学习,sklearn,高性能,大并发。</p>

        </div>'''
a=re.sub('</?p>|</?div>|<br>| ','',s)
print(a)

运行结果


岗位职责:
完成推荐算法、数据统计、接口、后台等服务器端相关工作

必备要求:
良好的自我驱动力和职业素养,工作积极主动、结果导向
&nbsp;
技术要求:
1、一年以上Python开发经验,掌握面向对象分析和设计,了解设计模式
2、掌握HTTP协议,熟悉MVC、MVVM等概念以及相关WEB开发框架
3、掌握关系数据库开发设计,掌握SQL,熟练使用MySQL/PostgreSQL中的一种
4、掌握NoSQL、MQ,熟练使用对应技术解决方案
5、熟悉Javascript/CSS/HTML5,JQuery、React、Vue.js
&nbsp;
加分项:
大数据,数理统计,机器学习,sklearn,高性能,大并发。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容