⛄正则表达式概述

正则表达式(Regular Expression)是强大、便捷、高效的文本处理工具。正则表达式本身,加上如同一门袖珍编程语言的通用模式表示法(general pattern notation),赋予使用者描述和分析文本的能力。配合上特定工具提供的额外支持,正则表达式能够添加、删除、分离、叠加、插入和修整各种类型的文本和数据。

正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。正则表达式是繁琐的,但它是强大的,学会之后的应用会让你除了提高效率外,会给你带来绝对的成就感。正则表达式的使用难度只相当于文本编辑器的搜索命令,但功能却与完整的文本处理语言一样强大。正则表达式的特点是:

  • 灵活性、逻辑性和功能性非常强;
  • 可以迅速地用极简单的方式达到字符串的复杂控制;
  • 对于刚接触的人来说,比较晦涩难懂。

RE语法中特殊元素

👀.匹配所有字符

.表示要匹配除了换行符之外的任何单个字符。其中点代表了任意的一个字符,注意是一个字符。

import re
str = re.compile(r".").findall("xiaotangsmile")
print(str)

# 输出结果
['x', 'i', 'a', 'o', 't', 'a', 'n', 'g', 's', 'm', 'i', 'l', 'e']

👀*重复匹配任意次

* 表示匹配前面的子表达式任意次,包括0次。

注意, .* 在正则表达式中非常常见,表示匹配任意字符任意次数。当然这个 * 前面不是非得是点,也可以是其它字符。

import re
str1 = re.compile(r"ia*").findall("xiaaaaaaaotangsmile")
print(str1)

# 输出结果
['iaaaaaaa', 'i']

👀+重复匹配多次

+表示匹配前面的子表达式一次或多次,不包括0次。即:表示至少匹配1次

import re
str2 = re.compile(r"ia+").findall("xiaaaaaaaotangsmile")
print(str2)

# 输出结果
['iaaaaaaa']

👀?匹配0-1次

? 表示匹配前面的子表达式0次或1次。

import re
str3 = re.compile(r"ia?").findall("xiaaaaaaaotangsmile")
print(str3)

# 输出结果
['ia', 'i']

👀{}匹配指定次数

{}花括号表示前面的字符匹配 指定的次数。

import re
str4 = re.compile(r"ia{2,5}").findall("xiaaaaaaaotangsmile")
print(str4)

# 输出结果
['iaaaaa']

👀[]匹配几个字符之一

[]方括号表示要匹配指定的几个字符之一。比如:

  • [abc] 可以匹配 a, b, 或者c里面的任意一个字符,等价于 [a-c];
  • [a-c] 中间的 -表示一个范围从a到c;
  • 如果你想匹配所有的小写字母,可以使用[a-z];
  1. 一些元字符在方括号内失去了本意,变得和普通字符一样了。例如:[evol.] 匹配 evol. 里面任意一个字符,这里 . 在括号里面不在表示匹配任意字符了,而就是表示匹配 . 这个字符。
  2. 如果在方括号中使用^,表示非方括号里面的字符集合。例如:[^\d]表示,选择非数字的字符。
import re
str5 = re.compile(r"i[a-z]").findall("xiaaaaaaaotangsmile")
print(str5)

# 输出结果
['ia', 'il']

👀贪婪模式和贪婪模式

在正则表达式中, ‘*‘, ‘+‘, ‘?‘ 都是贪婪地,使用他们时,会尽可能多的匹配内容。

解决这个问题,就需要使用非贪婪模式,也就是在星号后面加上? ,变成这样<.*?>

import re
str6 = re.compile(r"<.*>").findall("<html><head><title>Title</title>")
print(str6)
str7 = re.compile(r"<.*?>").findall("<html><head><title>Title</title>")
print(str7)

# 输出结果
['<html><head><title>Title</title>']
['<html>', '<head>', '<title>', '</title>']

👀元字符转义

反斜杠 \ 在正则表达式中有多种用途。

如果,我们这样写正则表达式 .*. ,聪明的你肯定发现不对劲。因为点是一个元字符,直接出现在正则表达式中,表示匹配任意的单个字符,不能表示.这个字符本身的意思了。

怎么办呢?如果我们要搜索的内容本身就包含元字符,就可以使用反斜杠进行转义。这里我们就应用使用这样的表达式: .*\.

import re
str8 = re.compile(r"[a-zA-Z0-9]*@[1-9]{3}\.com").findall("xiao@163.cn,tangsmile@126.com")
print(str8)
str9 = re.compile(r"[\w]*@[1-9]{3}\.com").findall("xiaoxiao@163.cn,tangsmile@126.com")
print(str9)

# 输出结果
['tangsmile@126.com']
['tangsmile@126.com']

👀特殊字符类

反斜杠后面接一些字符,表示匹配某种类型的一个字符。

实例 描述
\d 匹配任意一个数字字符。等价于[0-9]
\D 匹配任意一个非数字字符。等价于[^0-9]。
\s 匹配任意一个空白字符,包括空格、制表符、换页符等等。等价于[\f\n\r\t\v]。
\S 匹配任意一个非空白字符。等价于[^\f\n\r\t\v]。
\w 匹配任意一个文字字符,包括大小写字母、数字、下划线。等价于[A-Za-z0-9_]。
\W 匹配任意一个非文字字符。等价于[^A-Za-z0-9_]。

反斜杠也可以用在方括号里面,例如: [\s,.] 表示匹配 任何空白字符,或者逗号,或者点。

👀起始、结尾位置和单/行模式

^ 在方括号里面表示非;除此之外,^表示匹配文本的开头位置。

正则表达式可以设定单行模式和多行模式

  • 如果是单行模式,表示匹配整个文本的开头位置。
  • 如果是多行模式,表示匹配文本每行的开头位置。

$ 表示匹配文本的 结尾 位置。

  • 如果是单行模式,表示匹配整个文本的结尾位置。
  • 如果是多行模式,表示匹配文本每行的结尾位置。
import re
content = '''001-小汤-99
002-小明-98
003-小红-97'''
# 注意,compile 的第二个参数 re.M ,指明了使用多行模式,
str10 = re.compile(r'^\d+', re.M).findall(content)
print(str10)
str11 = re.compile(r'^\d+').findall(content)
print(str11)

# 输出结果
['001', '002', '003']
['001']
import re
content = '''001-小汤-99
002-小明-98
003-小红-97'''
str12 = re.compile(r'\d+$', re.M).findall(content)
print(str12)
str13 = re.compile(r'\d+$').findall(content)
print(str13)

# 输出结果
['99', '98', '97']
['97']

👀|竖线

竖线表示匹配其中之一 。特别要注意的是,竖线在正则表达式的优先级是最低的,这就意味着,竖线隔开的部分是一个整体。

比如,小明|王 表示要匹配:小明或者 ,而不是 小明 或者 小王

import re
str14 = re.compile(r'小汤|明|红|王').findall("小汤小明小红小王")
print(str14)

# 输出结果
['小汤', '明', '红', '王']

👀()括号

括号称之为正则表达式的组选择。组就是把正则表达式匹配的内容里面 中的某些部分标记为某个组。我们可以在 正则表达式中标记多个组。

为什么要有组的概念呢?因为我们往往需要提取已经匹配的内容里面的某些部分。

import re
content = '''001小汤,你最棒
002小明,你更棒
003小红,你较棒'''
# 输出包含逗号
str15 = re.compile(r'^.*,').findall(content)
print(str15)
# 输出不包含逗号
str16 = re.compile(r'^(.*),').findall(content)
print(str16)

# 输出结果
['001小汤,']
['001小汤']

👀点匹配换行符

点是不匹配换行符的,可是有时候,特征字符串就是跨行的,比如要找出下面文字中所有的职位名称。

设置点也匹配换行符:

# 使用re.S和re.DOTALL匹配出来的内容都是一样的,都表示包括换行符内容的匹配
# re.DOTALL
# re.S
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>
'''
str16 = re.compile(r'class=\"t1\">.*?<a>(.*?)</a>', re.S).findall(content)
print(str16)

# 输出结果
['Python开发工程师', 'java开发工程师']

⛄常用的函数

re模块使Python语言拥有全部的正则表达式功能。

  • compile()函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象,该对象拥有一系列方法用于正则表达式匹配和替换。
  • re 模块也提供了与这些方法功能完全一致的函数,这些函数使用一个模式字符串做为它们的第一个参数。

👀re.match()

re.match()尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,就返回none

# 函数语法
re.match(pattern, string, flags=0)
# 参数
pattern 匹配的正则表达式
string 要匹配的字符串。
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

匹配成功``re.match()方法**返回一个匹配的Match对象**,而不是匹配的正则表达式,否则返回None。通过span()可以获取匹配的位置,使用group(num)groups()` 匹配对象函数来获取匹配表达式。

import re
# 在起始位置匹配
print(re.match('www', 'www.runoob.com').span())
# 不在起始位置匹配
print(re.match('com', 'www.runoob.com'))

# 输出结果
(0, 3)
None

re.search()扫描整个字符串并返回第一个成功的匹配

# 函数语法
re.search(pattern, string, flags=0)
# 参数
pattern 匹配的正则表达式
string 要匹配的字符串。
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

匹配成功re.search()方法返回一个匹配的Match对象,而不是匹配的正则表达式,否则返回None。通过span()可以获取匹配的位置,使用group(num)groups() 匹配对象函数来获取匹配表达式。

import re
# 在起始位置匹配
print(re.search('www', 'www.runoob.com').span())
# 不在起始位置匹配
print(re.search('com', 'www.runoob.com').span())

# 输出结果
(0, 3)
(11, 14)

re.match()只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回 None,而re.search()匹配整个字符串,直到找到一个匹配。

👀re.sub()检索和替换

Pythonre模块提供了re.sub()用于替换字符串中的匹配项。

# 函数语法:
re.sub(pattern, repl, string, count=0, flags=0)
# 参数
pattern: 正则中的模式字符串。
repl: 替换的字符串,也可为一个函数。
string: 要被查找替换的原始字符串。
count: 模式匹配后替换的最大次数,默认0表示替换所有的匹配。
flags: 编译时用的匹配模式,数字形式。

# 前三个为必选参数,后两个为可选参数;返回替换后的字符串。
import re 
phone = "2004-959-559 # 这是一个电话号码"
# 删除注释
num = re.sub(r'#.*$', "", phone)
print ("电话号码 : ", num)
# 移除非数字的内容
num = re.sub(r'\D', "", phone)
print ("电话号码 : ", num)

# 输出结果
电话号码 : 2004-959-559
电话号码 : 2004959559

👀compile()

compile()函数用于编译正则表达式,生成一个正则表达式(Pattern)对象,供match()search()这两个函数使用。

# 语法格式
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 为了增加可读性,忽略空格和' # '后面的注释
import re 
>>> pattern = re.compile(r'([a-z]+) ([a-z]+)', re.I) # re.I表示忽略大小写
>>> m = pattern.match('Hello World Wide Web')
>>> print( m ) # 匹配成功,返回一个 Match 对象
<_sre.SRE_Match object at 0x10bea83e8>
>>> m.group(0) # 返回匹配成功的整个子串
'Hello World'
>>> m.span(0) # 返回匹配成功的整个子串的索引
(0, 11)
>>> m.group(1) # 返回第一个分组匹配成功的子串
'Hello'
>>> m.span(1) # 返回第一个分组匹配成功的子串的索引
(0, 5)
>>> m.group(2) # 返回第二个分组匹配成功的子串
'World'
>>> m.span(2) # 返回第二个分组匹配成功的子串索引
(6, 11)
>>> m.groups() # 等价于 (m.group(1), m.group(2), ...)
('Hello', 'World')
>>> m.group(3) # 不存在第三个分组
Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: no such group

👀findall()

在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果有多个匹配模式,则返回元组列表,如果没有找到匹配的,则返回空列表。

注意: match()和search()是匹配一次,findall()匹配所有。

# 语法格式
re.findall(pattern, string, flags=0)

pattern.findall(string[, pos[, endpos]])
# 参数
pattern 匹配模式。
string 待匹配的字符串。
pos 可选参数,指定字符串的起始位置,默认为 0
endpos 可选参数,指定字符串的结束位置,默认为字符串的长度。
# (1)查找字符串中的所有数字
import re
result1 = re.findall(r'\d+','runoob 123 google 456')
print(result1)
pattern = re.compile(r'\d+') # 查找数字
result2 = pattern.findall('runoob 123 google 456')
print(result2)
result3 = pattern.findall('run88oob123google456', 0, 10)
print(result3)

# 输出结果
['123', '456']
['123', '456']
['88', '12']

# 多个匹配模式,返回元组列表
import re
result = re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10')
print(result)

# 输出结果
[('width', '20'), ('height', '10')]

👀re.finditer()

findall()类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。

# 语法格式
re.finditer(pattern, string, flags=0)
# 参数
pattern 匹配的正则表达式
string 要匹配的字符串。
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
import re
it = re.finditer(r"\d+","12a32bc43jf3")
print(it)
for match in it:
print (match.group())

# 输出结果
<callable_iterator object at 0x000001FA199BA0C8>
12
32
43
3

👀re.split()

split()方法按照能够匹配的子串将字符串分割后,返回列表

# 语法格式
re.split(pattern, string[, maxsplit=0, flags=0])
# 参数
pattern 匹配的正则表达式
string 要匹配的字符串。
maxsplit 分割次数,maxsplit=1 分割一次,默认为 0,不限制次数。
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。
>>>import re 
>>> re.split('\W+', 'runoob, runoob, runoob.')
['runoob', 'runoob', 'runoob', '']
>>> re.split('(\W+)', ' runoob, runoob, runoob.')
['', ' ', 'runoob', ', ', 'runoob', ', ', 'runoob', '.', '']
>>> re.split('\W+', ' runoob, runoob, runoob.', 1)
['', 'runoob, runoob, runoob.']
>>> re.split('ab*', 'hello world') # 对于一个找不到匹配的字符串而言,split 不会对其作出分割
['hello world']

👀正则表达式对象

re.RegexObject

  • re.compile() 返回 RegexObject 对象。

re.MatchObject

  • group() 返回被 RE 匹配的字符串。

    • start() 返回匹配开始的位置

    • end() 返回匹配结束的位置

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

👀正则表达式修饰符-可选标志

正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位 OR(|) 它们来指定。如re.I | re.M 被设置成 I 和 M 标志:

修饰符 描述
re.I 使匹配对大小写不敏感
re.L 做本地化识别(local-aware)匹配
re.M 多行匹配,影响^和$
re.S 使.匹配包括换行在内的所有字符
re.U 根据Unicode字符集解析字符。这个标志影响\w\W\b\B
re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。

👀正则表达式实例

(1)字符匹配

实例 描述
python 匹配 “python“.

(2)字符类

实例 描述
[Pp]ython 匹配 “Python“ 或 “python
rub[ye] 匹配 “ruby“ 或 “rube
[aeiou] 匹配中括号内的任意一个字母
[0-9] 匹配任何数字。类似于 [0123456789]
[a-z] 匹配任何小写字母
[A-Z] 匹配任何大写字母
[a-zA-Z0-9] 匹配任何字母及数字
[^aeiou] 除了aeiou字母以外的所有字符
[^0-9] 匹配除了数字外的字符

(3)特殊字符类

实例 描述
. 匹配除 “\n“ 之外的任何单个字符。要匹配包括 ‘\n‘ 在内的任何字符,请使用象 ‘[.\n]‘ 的模式。
\d 匹配一个数字字符。等价于[0-9]。
\D 匹配一个非数字字符。等价于[^0-9]。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于[\f\n\r\t\v]。
\S 匹配任何非空白字符。等价于[^\f\n\r\t\v]。
\w 匹配包括下划线的任何单词字符。等价于[A-Za-z0-9_]。
\W 匹配任何非单词字符。等价于[^A-Za-z0-9_]。

笔者不才,请多交流!!!

参考文献:正则表达式 - 教程|菜鸟教程