【Python中的正则表达式 | re模块】

正则表达式(Regular Expression,通常简写为regex或regexp)是一种强大的文本处理工具,它使用一种特定的模式来描述和匹配字符串。正则表达式可以用于搜索、替换和验证文本中的特定字符序列

简单来说,可以理解为正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配

一、基本概念和常见元素

1、基本元素

  • 字符类:匹配方括号中的任意字符。例如,[abc] 匹配 “a”、“b” 或 “c”。
  • 范围:使用连字符 - 来表示字符范围。例如,[a-z] 匹配任何小写字母。
  • 否定字符类:在字符类前加上 ^ 符号可以匹配不在该字符类中的任意字符。例如,[^a-z] 匹配任何非小写字母的字符。
  • 元字符:具有特殊含义的字符,如 .(匹配任意字符,除了换行符)、*(匹配前面的元素零次或多次)、+(匹配前面的元素一次或多次)、?(匹配前面的元素零次或一次)等。
  • 锚点:用于指定匹配的位置,如 ^(匹配字符串的开头)和 $(匹配字符串的结尾)。

2、分组和引用

  • 圆括号:用于分组,并捕获匹配的子字符串
  • 反向引用:使用 \1、\2 等来引用之前捕获的分组。

3、修饰符和标志

  • 多行模式:使 ^ 和 $ 分别匹配每一行的开头和结尾,而不是整个字符串的开头和结尾。
  • 忽略大小写:使匹配时不区分大小写。
  • 全局搜索:搜索整个字符串中的所有匹配项,而不是只找到第一个就停止。

4、常见应用

  • 验证:检查字符串是否符合特定的格式或规则,如电子邮件地址、电话号码等。
  • 搜索和替换:在文本中查找特定的模式,并用其他文本替换它。
  • 分割字符串:使用正则表达式作为分隔符来分割字符串。

示例:

import re

text = "There are 123 apples and 456 oranges."
pattern = r'\d+'  # 匹配一个或多个数字

matches = re.findall(pattern, text)
print(matches)  # 输出: ['123', '456']

示例中, r'\d+' 是一个正则表达式,其中 \d 匹配任意数字, + 匹配前面的元素(即数字)一次或多次。re.findall() 函数用于在文本中查找所有匹配该正则表达式的子字符串。

二、python 正则表达式 re模块

re 模块使 Python 语言拥有全部的正则表达式功能。这个模块提供了一系列函数和方法,用于匹配和操作字符串。接下来将学习re模块的处理函数。

1、re模块的基本函数

>>> import re
>>> dir(re)
['A', 'ASCII', 'DEBUG', 'DOTALL', 'I', 'IGNORECASE', 'L', 'LOCALE', 'M', 'MULTILINE', 
'Match', 'NOFLAG', 'Pattern', 'RegexFlag', 'S', 'Scanner', 'T', 'TEMPLATE', 'U', 
'UNICODE', 'VERBOSE', 'X', '_MAXCACHE', '_MAXCACHE2', '__all__', '__builtins__',
 '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', 
 '__path__', '__spec__', '__version__', '_cache', '_cache2', '_casefix',
  '_compile', '_compile_template', '_compiler', '_constants', '_parser',
   '_pickle', '_special_chars_map', '_sre', 'compile', 'copyreg', 'enum',
    'error', 'escape', 'findall', 'finditer', 'fullmatch', 'functools', 
    'match', 'purge', 'search', 'split', 'sub', 'subn', 'template']
>>>

和其他模块一样,在使用re模块来处理正则表达式时,要先导入 re 模块

import re

2、re模块函数使用

正则表达式的语法和功能非常强大和灵活,可以通过学习更多的元字符、量词、边界匹配来扩展正则表达式的知识。
(1)re.match()
从字符串的起始位置匹配一个模式,如果匹配成功,返回一个匹配对象;否则返回None

语法:

re.match(pattern, string, flags=0)

参数说明–
pattern:匹配的正则表达式
string :要匹配的字符串。
flags :标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 - 可选标志

匹配对象方法:可使用 group(num)groups() 匹配对象函数来获取匹配表达式。

  • group(num=0) 匹配的整个表达式的字符串,group()可一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。
  • groups() 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。

示例如下:

import re

line = "Cats are smarter than dogs"

matchObj = re.match(r'(.*) are (.*?) than (.*)', line)
if matchObj:
    print("matchObj.group() : ", matchObj.group())  #输出 Cats are smarter than dogs
    print("matchObj.groups() : ", matchObj.groups()) #输出 ('Cats', 'smarter', 'dogs')
    print("matchObj.group(1) : ", matchObj.group(1)) #输出 Cats
    print("matchObj.group(2) : ", matchObj.group(2)) #输出 smarter
    print("matchObj.group(3) : ", matchObj.group(3)) #输出dogs

else:
    print("No match!!")

以上示例,正则表达式:r'(.*) are (.*?) than (.*)'
其中 (.*):匹配任意数量的任意字符(除了换行符),并将它们捕获到第一个组中。
(.*?):匹配任意数量的任意字符(除了换行符),但尽可能少地匹配(非贪婪模式),并将它们捕获到第二个组中。

(2)re.search()
扫描整个字符串,返回第一个成功匹配的对象。
语法:

re.search(pattern, string, flags=0)
  • re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None。
  • re.search匹配整个字符串,直到找到一个匹配。
import re
print(re.match('hello', 'hello.python.com').span())  # 在起始位置匹配   输出:(0, 5)
print(re.match('com', 'hello.python.com'))         # 不在起始位置匹配   输出:None

print(re.search('hello', 'hello.python.com').span())  # 在起始位置匹配   输出:(0, 5)
print(re.search('com', 'hello.python.com').span())    # 不在起始位置匹配  输出:(13, 16)

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

注意: match 和 search 是匹配一次 findall 匹配所有。
语法:

findall(string, pos, endpos])

参数说明:
string : 待匹配的字符串。
pos : 可选参数,指定字符串的起始位置,默认为 0。
endpos : 可选参数,指定字符串的结束位置,默认为字符串的长度。

示例:

import  re
pattern = re.compile(r'\d+') # 匹配一个或多个数字
text= "I have 100 leomon trees and 20 rivers"
findall= pattern.findall(text)
findall1= pattern.findall(text,0,10)
print("findall:",findall)  #输出:findall: ['100', '20']
print("findall1:",findall1) #输出:findall1: ['100']

(4)re.finditer()
找到字符串中所有匹配的部分,并返回一个迭代器,产生匹配对象。
语法:

re.finditer(pattern, string, flags=0)

示例:

import  re
pattern = re.compile(r'\d+') # 匹配一个或多个数字
text= "I have 100 leomon trees and 20 rivers"
findall= pattern.finditer(text)
findall1= pattern.finditer(text,0,10)
for match in findall:
    print (match.group() )
for match in findall1:
    print (match.group() )

运行结果:

100
20
100

(5)re.sub():检索和替换
在字符串中查找匹配正则表达式的部分,并用另一个字符串替换它们。
语法:

re.sub(pattern, repl, string, count=0, flags=0)

参数说明:
pattern : 正则中的模式字符串。
repl : 替换的字符串,也可为一个函数。
string : 要被查找替换的原始字符串。
count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。

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

re.split(pattern, string, maxsplit=0, flags=0])

参数说明:

  • pattern, string同上。
  • maxsplit 分隔次数,maxsplit=1 分隔一次,默认为
    0,不限制次数。
  • flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 - 可选标志

(7)re.compile():
编译正则表达式,根据一个模式字符串和可选的标志参数生成一个正则表达式( Pattern )对象。
即:预编译一个正则表达式对象,然后重复使用它。
该对象拥有一系列方法用于正则表达式匹配、搜索、替换等操作。

语法:

re.compile(pattern, flags=0)

① 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 为了增加可读性,忽略空格和 # 后面的注释

编译后的Pattern对象可以使用的方法包括match()、search()、findall()、finditer()、sub()、split()等
示例:

import re

# 编译一个正则表达式
pattern = re.compile(r'\d+')  # 匹配一个或多个数字

# 使用编译后的正则表达式对象进行匹配
text = "123 apples and 456 oranges."

# 使用match方法从字符串的起始位置匹配
match = pattern.match(text)
if match:
    print("Match at start:", match.group()) #输出 Match at start: 123
else:
    print("No match at start.")

# 使用search方法在整个字符串中搜索匹配项
search = pattern.search(text)
if search:
    print("Match found:", search.group()) # 输出: Match found: 123
else:
    print("No match found.")

# 使用findall方法找到所有匹配项
matches = pattern.findall(text)
print("All matches:", matches)  # 输出: All matches: ['123', '456']

compile()函数的好处:可将正则表达式的编译和匹配操作分开,多次重用编译后的正则表达式对象,提高性能。使代码更清晰、更易于维护。

(8)修饰符和标志
在编译正则表达式时,可以使用一些标志来改变匹配的行为。例如,re.IGNORECASE或re.I用于忽略大小写匹配。

示例如下:

import re

print("----re.match()匹配----")
pattern = r'\d+'  #匹配一个或多个数字
text = "123 apples and 456 oranges"
match = re.match(pattern, text)
if match:
    print("Found:", match.group())  # 输出: Found: 123


print("----re.search()扫描----")
match = re.search(pattern, text)
if match:
    print("Found:", match.group())  # 输出: Found: 123


print("----re.findall()函数----")#找到字符串中所有匹配的部分,并返回一个列表
matches = re.findall(pattern, text)
print(matches)  # 输出: ['123', '456']


print("----re.finditer()函数----")
#找到字符串中所有匹配的部分,并返回一个迭代器,产生匹配对象。
matches = re.finditer(pattern, text)
for match in matches:
    print(match.group())  # 分别输出: 123 和 456


print("----re.sub()替换----")
new_text = re.sub(r'\d+', 'XXX', text)
print(new_text)  # 输出: XXX apples and XXX oranges


print("----re.split()分割----")
words = re.split(r'\d+', text)
print(words)  # 输出: ['', ' apples and ', ' oranges']


print("----re.compile()预编译----")
compiled_pattern = re.compile(r'\d+')
matches = compiled_pattern.findall(text)
print(matches)  # 输出: ['123', '456']


print("----re.IGNORECASE或re.I用于忽略大小写匹配----") #修饰符和标志
pattern = re.compile(r'apple', re.IGNORECASE)
match = pattern.search('There is an Apple on the table.')
if match:
    print("Found:", match.group())  # 输出: Found: Apple

三、应用

‘(?P…)’ 分组匹配
示例1:日期"The date is 2024-04-18."

import re

# 定义一个包含日期的字符串
date_string = "The date is 2023-03-15."
# 使用命名分组来匹配日期
pattern = r"The date is (?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})."
# 执行匹配
match = re.search(pattern, date_string)
# 如果匹配成功,打印命名分组的值
if match:
    print("match.group:", match.group())
    print("group-Year:", match.group("year"))
    print("group-Month:", match.group("month"))
    print("group-Day:", match.group("day"))

    print("match.groups:", match.groups())
    print("groups-Year:", match.groups("year"))
    print("groups-Month:", match.groups("month"))
    print("groups-Day:", match.groups("day"))

    print("match.groupdict:", match.groupdict())

else:
    print("No match found.")

运行结果:

match.group: The date is 2023-03-15.
group-Year: 2023
group-Month: 03
group-Day: 15
match.groups: ('2023', '03', '15')
groups-Year: ('2023', '03', '15')
groups-Month: ('2023', '03', '15')
groups-Day: ('2023', '03', '15')
match.groupdict: {'year': '2023', 'month': '03', 'day': '15'}

示例2:识别身份证ID
在Python的re模块中,当使用命名分组(即(?P…)语法)进行正则表达式匹配时,匹配对象会提供一个名为groupdict()的方法。这个方法返回一个字典,其中的键是命名分组的名称,值是相应分组匹配到的字符串。这对于提取和访问匹配到的命名分组非常有用。

import re

id = '4102211990xxxxxxxx'
res = re.search(r'(?P<province>\d{3})(?P<city>\d{3})(?P<born_year>\d{4})', id[:10])
if res:
    print(res.groupdict())#输出{'province': '410', 'city': '221', 'born_year': '1990'}
else:
    print("No match found.")

or

import re
id = '4102211990xxxxxxxx'
res = re.search(r'(?P<province>\d{3})(?P<city>\d{3})(?P<born_year>\d{4})',id)
print(res.groupdict())
上一篇:正则表达式详解


下一篇:以Kotti项目为例使用pytest测试项目