正则表达式

  • 介绍:在实际开发中经常会有查找某些复杂规则的字符串的需求,比如邮箱、图片地址、手机号码等,这时候想要匹配或者查找某些规则的字符串就可以使用正则表达式了。
  • 正则表达式是用于处理字符串的强大工具,其他编程语言中也有正则表达式的概念,区别只在于不同的编程语言支持的语法数量不同。
  • 概念:正则表达式(或RE)是一种小型的、高度专业化的编程语言,正则表达式就是记录文本规则的代码,它内嵌在Python中,并通过re模块实现。
  • 特点:
  1. 正则表达式的语法很令人头疼,可读性差;
  2. 正则表达式通用性很强,能够适用很多编程语言;
  3. 正则表达式拥有自己独特的语法以及一个独立的处理引擎(正则表达式模式被编译成一系列的字节码,然后由用C编写的匹配引擎执行),在提供了正则表达式的语言里,正则表达式的语法都是一样的。
  4. 正则表达式语言相对小型和受限(功能有限);
  5. 并非所有字符串处理都能用正则表达式完成。

反斜杠的麻烦

In [1]: "hello\nworld"   # Out[1]: 'hello\nworld'
In [2]: r"hello\nworld"  # Out[2]: 'hello\\nworld'

In [3]: a = "hello\nwordl"
In [4]: b = r"hello\nworld"

In [5]: print(a)
hello
wordl

In [6]: print(b)
hello\nworld

扩展:“\

  1. 用于取消所有的元字符(转译为普通符号),如:
  1. []”在正则中表示列举的字符集,如果仅仅想匹配“[]”符号,可以使用“\[\]”将其转译为普通符号。
  2. .”在正则总表示除\n以外的任意一个字符,如果仅仅想匹配“.”符号,可以使用“\.”将其转译为普通符号。
  1. 后面可以加不同的字符以表示不同特殊含义,如:
  1. dDwW等表示特殊的字符集,具体看后面的表;
  2. 加数字,表示引用的分组编号,看下面的“匹配分组”;
  3. nt等表示换行、缩进等。

正则匹配的函数

  • RegexObject实例有一些方法和属性,完整的列表可查询Python Library Reference:
  • 如果没有匹配成功的话,match()search()将返回None;
  • 如果成功的话,就会返回一个MatchObject对象;

方法

作用

re.match(r"", str[,flags])

根据正则表达式从头开始匹配字符串数据

re.search(r"", str[,flages])

根据正则表达式从头到尾匹配字符串数据,匹配1次

re.findall()

找到RE匹配的所有字串,返回一个列表(存放匹配结果字符串的列表)

re.finditer()

找到RE匹配的所有字串,返回一个迭代器(存放匹配结果MatchObject对象的迭代器)

re.split(r"","text")

按照RE分割字符串

re.sub(r"","new","old"[,count])

根据REnew替换掉old字符串对应规则的部分,count表示替换次数,不设置默认替换所有,最后返回替换后的字符串

re.subn(r"","new","old")

根据REnew字符串替换掉old字符串负责规则的部分,返回(替换后的字符串, 替换次数)

sub():正则替换字符串

rs = r'c..t'
re.sub(rs, 'python', 'csvt caat cvvt cccc')
-------------------------------------------
结果:'python python python cccc'
  • replace():字符串方法,替换字符串,会把正则表达式当成普通的字符串。
'csvt caat cvvt c..t cccc'.replace('c..t', 'python')
-------------------------------------------------------------
结果:'csvt caat cvvt python cccc'  # replace()不认识正则表达式

subn()替换字符串,并返回替换了几次

rs = r'c..t'
re.subn(rs, 'python', 'csvt caat cvvt cccc')
结果:('python python python cccc', 3)

MatchObject实例方法

方法

作用

obj.group()

返回被RE匹配的字符串

obj.start()

返回匹配开始的位置

obj.end()

返回匹配结束的位置

obj.span()

返回一个元组包含匹配(开始,结束)的位置

  • 实际程序中,最常见的做法是将MatchObject保存在一个变量里,然后检查它是否为空。
p = re.compile(...)
m = p.match('string goes here')
if m:
    # group()参数代表索引,从1开始。
    # 0或不写代表返回所有,1代表返回第一个分组,2代表返回第二个分组
    print('匹配成功:', m.group())
    print('匹配成功:', m.group(0))
    print('匹配成功:', m.group(1))
    print('匹配成功:', m.group(2))
else:
    print('匹配失败')

匹配普通字符

  • 大多数字母和字符一般都会和自身匹配,如正则表达式r"hello"会和字符串"hello"完全匹配。

匹配单个字符(元字符)

代码

功能

.

匹配任意1个字符(除了\n

[]

匹配[]中列举的字符,注意:元字符在字符集中不起作用,如[akm$]:把$当成要匹配的字符了

[^]

不在[]中的字符,如:[^abc]匹配除了a,b,c之外的字符

[-]

匹配XY之间的某个字符。[0-9]匹配0到9之间的任一数字;[a-z]匹配az之间的任一小写字母;[A-Z]匹配AZ之间的任一大写字母

\d

匹配十进制数字,即[0-9]

\D

匹配任何非数字字符,即[^0-9]

\s

匹配任何空白,即[\t\n\r\f\v]如:空格、换行、tab键等

\S

匹配任何非空白,即[^\t\n\r\f\v]

\w

匹配非特殊字符,即a-zA-Z0-9_、汉字

\W

匹配特殊字符,即非字母、非数字、非汉字、非下划线

匹配多个字符(量字符)

代码

功能

*

匹配前一个字符出现0次或者无限次,不超过整数界定范围20亿{0,}

+

匹配前一个字符出现1次或者无限次,即至少有1次{1,}

?

匹配前一个字符出现1次或者0次,表示某件事物是可选的{0,1}

{m}

匹配前一个字符出现m

{m,n}

匹配前一个字符出现从mn次(闭区间);忽略m{,n}会认为下边界是0;忽略n:{m,}的觉过将是上边界为无穷大;注意:{}中不要乱加空格

扩展:“?

  • ?加在+*后面做的是最小匹配,如:
# + 与 ? 组合,如果不加?返回的就是['abbbbbbbb']
re.findall('ab+?','abbbbbbbb') 
结果:['ab']
# * 与 ? 组合, 如果不加?返回的就是['abbbbbbbb']
re.findall('ab*?','abbbbbbbb')
结果:['a']

匹配开头和结尾

代码

功能

^

匹配字符串开头[^指定字符]: 表示除了指定字符都匹配

$

匹配字符串结尾

匹配分组

代码 | 功能
| | 匹配左右任意一个表达式
() | 作用一:在全匹配中优先输出()中的内容,常用于爬虫;作用二:将括号中字符作为一个成为整体(分组)
\num | 引用分组num匹配到的字符串(内容),如果正则前没写r,就要写成\\num,即r"\num"<==>"\\num"注意:匹配的是内容,不是规则
(?P<name>) | 分组起别名
(?P=name) | 引用别名为name分组匹配到的字符串

()的作用一:在全匹配中优先输出()中得内容

s = '''hhsdj dskj hello src=csvt yes jdjsag
djcasgad src=123 yes jdsa
src=234 yes
hello src=pyhon yse kasdg '''

r1 = r"hello src=(.+ yes)"
re.findall(r1, s)
# 结果:['csvt yes', 'python yes']

()的作用二:让正则中得一部分成为整体,即分组

# 普通分组:邮箱结尾是.com或.cn,如果不加(),那正则就成了xxx.m或.cn了
email = r"\w{3}@\w+(\.com|\.cn)"
re.match(email, zzz@xx.com')
re.match(email, 'zz@yy.cn')

匿名分组

  1. 创建正则的分组:用户创建的分组编号是从1开始的;
  2. 分组引用:将分组匹配到的数据在正则中使用,“\编号”表示引用第几个分组的数据。
  • 缺点:分组发生变化,分组的编号全部需要修改。
# 需求:匹配出<html>hh</html>
# 原始写法
match_obj = re.match("<[a-zA-Z1-6]+>.*</[a-zA-Z1-6]+>", "<html>hh</html>")
# 利用匿名分组的分组编号简化正则
match_obj = re.match(r"<([a-zA-Z1-6]+)>.*</\1>", "<html>hh</html>")
if match_obj:
    print(match_obj.group())
    print(match_obj.group(0))  # 等价于上面语句,0表示完整的匹配结果
    print(match_obj.group(1))  # 表示第一组正则表达式,这里输出的是“html”
else:
    print("匹配失败")

有名分组

  1. 创建有名分组:(?P<name>正则)
  2. 使用有名分组的引用:(?P=name)
# 需求:匹配出<html><h1>www.itcast.cn</h1></html>
match_obj = re.match(""""<(?P<name1>[a-zA-Z1-6]+)><(?P<name2>
[a-zA-Z1-6]+)>.*
</(?P=name2)></(P=name1)>""", 
"<html><h1>www.itcast.cn</h1></html>", re.X)  # re.X表示支持将正则写成多行,下面有讲

if match_obj:
    print(match_obj.group())
else:
    print("匹配失败")

变异标志-flags

参数

含义

re.DOTALL

简写re.S 使.匹配包括\n\t等在内的所有字符

re.IGNORECASE

简写re.I 使匹配不区分大小写

re.LOCALE

简写re.L 做本地化识别(locale-awaer)匹配.法语等“”或“é

re.MULTILINE

简写re.M 多行匹配,影响^$

re.VERBOSE

简写re.X 能够使用REsverbose状态,使之被组织得更清晰易懂(就是正则写成多行得)

re.VERBOSEre.X参数:正则表达式可以写成多行

charref = re.compile(r'''
(
[0-9]+[^0-9]                # Decimal form
|0[0-7]+[^0-7]              # Octal form
|x[0-9a-fA-F]+[^0-9a-fA-F]  # Hexadecimal form
)
''', re.VEROSE)

正则预编译re.compile()

  • re模块提供了一个正则表达式引擎的接口,可以让你将REstring编译成对象,并用他们来进行匹配,这样速度比直接利用正则匹配速度快
    编译正则表达式及使用方式:
import re 
tel = r"010-?\d{8}$"
p_tel = re.compile(tel)     # 将正则表达式生成一个对象
result_list = p_tel.findall("010-12345678")      # 使用方式一
result_list = re.findall(p_tel, "010-12345678")  # 使用方式二

re.compile()也可以接受可选的标志参数,常用来实现不同的特殊功能和语法变更,比如忽略大小写:

p = re.compile('ab*', re.IGNORECASE)