正则表达式

正则表达式

1.正则表达式简介

1.1 什么是正则表达式

正则表达式(Regular Expression,简称regex)是一种用于描述字符串匹配模式的强大工具。它是一种特殊的字符序列,可以帮助你方便地检查一个字符串是否与某种模式匹配,或者从字符串中提取满足某种模式的子串。

1.2 正则表达式的应用场景

  • 数据验证 :如邮箱、手机号、身份证号等格式验证
emails = ["user@example.com", "invalid-email", "user.name@company.co.uk"]
  • 数据提取 :从文本中提取特定格式的信息
text = "联系方式:13812345678,固话:010-12345678,客服:400-123-4567"
  • 数据替换 :替换文本中的特定内容
text = "这个产品很垃圾,客服态度也很差劲!"
  • 数据分割 :按照特定模式分割字符串
text = "姓名:张三;年龄:25;职业:工程师"

1.3 正则表达式的优势

  • 灵活性 :可以描述各种复杂的字符串模式
  • 简洁性 :用简短的表达式描述复杂的匹配规则
  • 高效性 :相比于传统的字符串处理方法更加高效

2.正则表达式语法

2.1 基本字符匹配

字符描述
普通字符匹配自身,如 a 匹配字符"a"
.匹配除换行符外的任意一个字符
\转义字符,用于匹配特殊字符本身

案例:基本字符匹配

import re
# 原生字符串:所有字符都按照字符本身的描述进行打印 略过字符的特殊含义
print(r"ab\ncd")
print("ab\ncd")

text = "Hello World! World H1llo Hwllo H#llo H123llo"
# 普通字符匹配 search从头开始 第一次出现的匹配
# 含义:寻找 World 是否出现在 text中
match = re.search(r'World', text)
print(match.group())

# 点号匹配
arr = re.findall(r'H.llo', text)
print(arr)
arr = re.findall(r'H...llo', text)
print(arr)

text = "The coat is $100. $1204 $123213"
match = re.search(r'\$\d+', text)
print(match)
arr = re.findall(r'\$\d+', text)
print(arr)
text = "The coat is ¥100. ¥1204 ¥123213"
arr = re.findall(r'¥\d+', text)
print(arr)

2.2 字符类

表达式描述
[abc]匹配方括号内的任意字符
[^abc]匹配除了方括号内字符的任意字符
[a-z]匹配指定范围内的任意字符
\d匹配任意数字,等价于 [0-9]
\D匹配任意非数字,等价于 [^0-9]
\w匹配任意字母、数字或下划线,等价于 [a-zA-Z0-9_]
\W匹配任意非字母、数字或下划线,等价于 [^a-zA-Z0-9_]
\s匹配任意空白字符(空格、制表符、换行符等)[ \t\n\r\f\v]
\S匹配任意非空白字符[^ \t\n\r\f\v]

案例:字符类

import re
text = "The quick brown fox jumps over the lazy dog."
# 匹配所有的元音字母
arr = re.findall(r'[aeiou]', text.lower())
print(arr)
# 匹配所有的非元音字母
arr = re.findall(r'[^aeiou\s\.]', text.lower())
print(arr)

arr = re.findall(r'\d', "Phone: 123-456-789")
print(arr)

arr = re.findall(r'\w+', text)
print(arr)

2.3 边界匹配

表达式描述
^匹配字符串的开头
$匹配字符串的结尾
\b匹配单词边界
\B匹配非单词边界

案例:边界匹配

import re
print(re.search(r'^Python', "Python is cool"))
print(re.search(r'^Python', "I love python"))
print(re.search(r'Python$', "Python is cool"))
print(re.search(r'Python$', "I love Python"))

text = "Python programing is fun. I love Python!"
arr = re.findall(r'\bPython\b', text)
print(arr)

text = "word boundary example wordstart wordhaha xixiword endword awordlala abc123"
arr = re.findall(r'word\w+', text)
print(arr)
arr = re.findall(r'\bword\w+', text)
print(arr)

arr = re.findall(r'\w+word\b', text)
print(arr)

arr = re.findall(r'\b\w+\b', text)
print(arr)
arr = re.findall(r'\b[a-zA-Z]+\b', text)
print(arr)

2.4 数量限定符

量词描述示例
*匹配0次或多次ab* 匹配 “a”、“ab”、“abb” 等
+匹配1次或多次ab+ 匹配 “ab”、“abb”,但不匹配 “a”
?匹配0次或1次ab? 匹配 “a” 或 “ab”
{n}精确匹配n次a{3} 匹配 “aaa”
{n,}匹配至少n次a{2,} 匹配 “aa”、“aaa” 等
{n,m}匹配n到m次(包含n和m)a{2,3} 匹配 “aa” 或 “aaa”

案例:数量限定符

import re
text = "a ab abb abbb abbbb abbbbb abbbbbb abbbbbbb"
# a后面有>=0个b
arr = re.findall(r'ab*', text)
print(arr)
# a后面有>=1个b
arr = re.findall(r'ab+', text)
print(arr)
# a后面有0个或1个b
arr = re.findall(r'ab?', text)
print(arr)
# a后面有3个b
arr = re.findall(r'ab{3}', text)
print(arr)
# a后面有>=4个b
arr = re.findall(r'ab{4,}', text)
print(arr)
# a后面有2~4个b
arr = re.findall(r'ab{2,4}', text)
print(arr)

案例:验证邮箱格式

import re

def is_valid_email(email):
    pattern = r'^[\w\.-]+@[\w\.-]+\.[a-zA-Z]{2,}$'
    return bool(re.match(pattern, email))

emails = ["user@example.com", "invalid-email", "user.name@company.co.uk"]
for email in emails:
    print(f'{email}:{is_valid_email(email)}')

3.Python中的re模块

3.1 re模块简介

Python的 re 模块提供了对正则表达式的支持,它包含了许多用于字符串处理的函数和方法。

3.2 re模块的主要函数

函数描述
re.match()从字符串的起始位置匹配,如果起始位置匹配成功,返回Match对象,否则返回None
re.search()扫描整个字符串,找到第一个匹配的位置,返回Match对象,否则返回None
re.findall()返回字符串中所有与模式匹配的子串列表
re.finditer()返回一个迭代器,包含所有匹配的Match对象
re.sub()替换字符串中与模式匹配的子串
re.split()按照模式分割字符串
re.complie()编译正则表达式模式,返回一个Pattern对象

3.3 Match对象的主要方法

方法描述
group()返回一个或多个分组匹配的字符串
groups()返回一个包含所有分组的元组
groupdict()返回一个包含所有命名分组的字典
start()返回指定分组匹配的子串的起始位置
end()返回指定分组匹配的子串的结束位置
span()返回一个元组(start, end),表示指定分组匹配的子串的起始和结束位置

案例:使用re.match和re.search的区别

import re
text = "Python is awesome, Python is powerful"

# 简单理解 以XXX开头
match = re.match(r'Python', text)
print(match)

match = re.match(r'awesome', text)
print(match)

# 简单理解 检测XXX的存在性
match = re.search(r'powerful', text)
print(match)
match = re.search(r'java', text)
print(match)
match = re.search(r'Python', text)
print(match)

案例:使用re.findall和re.finditer

import re
text = "Python的版本有:Python2.7, Python3.6, Python3.8, Python3.9, Java8.8"

arr = re.findall(r'Python\d\.\d', text)
print(arr)

arr = re.findall(r'\d\.\d', text)
print(arr)

for match in re.finditer(r'Python(\d(\.\d))', text):
    print(f'在位置{match.start()}:{match.end()}找到:{match.group(0)},版本号:{match.group(1)},=={match.group(2)}')


text = "联系方式:电话15388886868,邮箱zh@oupeng.com,微信wx_123456"
# | 逻辑或 匹配任意一个模式
arr = re.findall(r'1[3-9]\d{9}|[a-zA-Z0-9\._%+-]+@[\w\.-]+\.[a-zA-Z]{2,}|wx_[a-zA-Z\d]+', text)
print(arr)

for match in re.finditer(r'1[3-9]\d{9}|[a-zA-Z0-9\._%+-]+@[\w\.-]+\.[a-zA-Z]{2,}|wx_[a-zA-Z\d]+', text):
    print(match.group())
    print(match.span())

案例:使用re.sub进行替换

import re
text = "这个产品很垃圾,客服态度也很差劲!这他妈的是个傻逼,草泥马哦"
sensitive_words = ["垃圾", "差劲","傻逼","草泥马","约吗","CP","他妈的"]

for word in sensitive_words:
    text = re.sub(word, "*" * len(word), text)
print(text)
# MM/DD/YYYY -> YYYY-MM-DD
# 用于拼接返回替代的字符串
def date_converter(match):
    # (06)/(04)/(2025)
    # 解析匹配字符串中的内容
    month, day, year = match.groups()
    return f'{year}-{month}-{day}'

text = "今天是06/04/2025, 明天是06/05/2025"
result = re.sub(r'(\d{2})/(\d{2})/(\d{4})', date_converter ,text)
print(result)



def date_converter_other(match):
    month, day, year = tuple(match.group().split("/"))
    return year + "-" + month + "-" + day

text = "今天是06/04/2025, 明天是06/05/2025"
result = re.sub(r'\d{2}/\d{2}/\d{4}', date_converter_other ,text)
print(result)

案例:使用re.split分割字符串

import re
text = "apple,banana;cherry|grape:orange"
# 切割返回的是一个列表
result = re.split(r'[,;|:]', text)
print(result)
# 限制分割次数
result = re.split(r'[,;|:]', text, maxsplit=2)
print(result)

text = "姓名:张三;年龄:25;职业:工程师"
parts = re.split(r'[;:]', text)
print(parts)

# 把切割出来的结果 进行字典化
keys = parts[::2] # 偶数位(角标)
print(keys)
values = parts[1::2] # 奇数位(角标)
print(values)
# 通过zip将两个列表进行打包 -> 一堆元组(键,值)
# 通过dict()函数 将一堆元组 进行字典化处理
person_dict = dict(zip(keys, values))
print(person_dict)

案例:使用re.compile预编译正则表达式

import re
import time
# 原本的正则表达式
pattern_str = r'^[\w\.-]+@[\w\.-]+\.[a-zA-Z]{2,}$'
# 被编译后的正则表达式-Pattern对象
email_pattern = re.compile(pattern_str)

# 创建一个大量的邮箱数据
emails = [f'user{i}@example.com' for i in range(10000)]

# 使用预编译模式
start_time = time.time()
valid_emails = [email for email in emails if email_pattern.match(email)]
end_time = time.time()
print(f'预编译方式耗时:{end_time - start_time:.6f}秒')

# 不使用预编译
start_time = time.time()
valid_emails = [email for email in emails if re.match(pattern_str, email)]
end_time = time.time()
print(f'非预编译方式耗时:{end_time - start_time:.6f}秒')

"""
Pattern.math(text) -> 匹配结果
re.match(pattern,text) -> Pattern.math(text) -> 匹配结果
"""

4.正则表达式分组和捕获

4.1 基本分组

使用圆括号()可以将正则表达式的一部分括起来形成一个分组。分组的作用:

  • 将多个字符视为一个整体,应用量词
  • 提取匹配结果中的特定部分
  • 允许在正则表达式内部或外部引用分组内容

案例:基本分组

import re

# 提取年月日
date_str = "今天是2025-06-05,明天是2025-06-06"
pattern = r'(\d{4})-(\d{2})-(\d{2})'

for match in re.finditer(pattern, date_str):
    print("完整的日期:", match.group())
    print("完整的日期:", match.group(0)) # 完整的匹配
    print("年份:", match.group(1))
    print("月份:", match.group(2))
    print("日期:", match.group(3))
    print("所有的分组:", match.groups())

案例:从文本中提取所有电话号码

import re
text = "联系方式:15388886666,固话:010-12345678,客服:400-123-4567"
pattern = r'(1[3-9]\d{9}|\d{3,4}-\d{7,8}|\d{3,4}-\d{3,4}-\d{4})'

phone_numbers = re.findall(pattern, text)
print(phone_numbers)

for match in re.finditer(pattern, text):
    print(match.group()) # 完全匹配
    print(match.group(0))# 完全匹配
    print(match.group(1))# 取分组中的内容 跟上述是一致的

4.2 命名分组

以通过(?P<name>...)语法为分组命名

案例:命名分组

import re
log_entry = "[2025-06-05 19:59:56] ERROR: Datebase connection failed"
pattern = r'\[((?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2}) (?P<time>\d{2}:\d{2}:\d{2}))\] (?P<level>\w+): (?P<message>.+)'

match = re.search(pattern, log_entry)
# 确保匹配成功
if match:
    print(match.group()) # 完全匹配
    print(match.group(0)) # 完全匹配
    print(match.group(1))
    print(match.group(2))
    print(match.group(6))
    # 就是为了避免角标操作
    print(match.group('year'))
    print(match.group('month'))
    print(match.group('day'))
    print(match.group('time'))
    print(match.group('level'))
    print(match.group('message'))

4.3 反向引用

在正则表达式内部,可以使用\数字\g<name>引用前面定义的分组

案例:反向引用

import re
# 匹配重复单词
text = "hello hello world world"
pattern = r'\b(\w+)\s+\1\b'
# findall 如果正则表达式中没有分组 则默认全匹配 有分组 则寻找分组内容
result = re.findall(pattern, text)
print(result)

# 提取HTML标签内容
html = "<h1>标题</h1><p>段落内容1</p><p>段落内容2</p>"
pattern = r'<([a-z0-9]+)>(.*?)</\1>'
result = re.findall(pattern, html)
print(result)

# 使用命名反向引用
text = "2025-06-05" # -> 06/05/2025
pattern = r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})'

text = re.sub(pattern, r'\g<month>/\g<day>/\g<year>' , text)
print(text)

5.正则表达式修饰符

5.1 常用修饰符

修饰符描述
re.Ire.IGNORECASE忽略大小写
re.Mre.MULTILINE多行模式,^$匹配每行的开头和结尾
re.Sre.DOTALL点号.匹配所有字符,包括换行符
re.Xre.VERBOSE允许使用注释和空格,使正则表达式更易读

5.2 内联修饰符

可以在正则表达式内部使用 (?imsxau) 语法应用修饰符。

案例:忽略大小写

import re

text = "Python is awesome. PYTHON is powerful. python is easy to learn."
# 不忽略大小写
print(re.findall(r'python', text))
# 忽略大小写
print(re.findall(r'python', text, re.IGNORECASE))
# 内联修饰符
print(re.findall(r'(?i)python', text))

案例:多行模式

import re

text = """第一行开始
第二行开始
第三行开始
结束
"""
print(re.findall(r'^第.+行', text))
print(re.findall(r'^第.+行', text, re.MULTILINE))
print(re.findall(r'(?m)^第.+行', text))

案例:点号匹配所有字符

import re

text = """这是第一行
这是第二行
   这是第三行
"""
match = re.search(r'第一行.*第三行', text)
print(match.group() if match else None)

match = re.search(r'第一行.*第三行', text, re.DOTALL)
print(match.group() if match else None)

match = re.search(r'(?s)第一行.*第三行', text)
print(match.group() if match else None)

6.贪婪与非贪婪匹配

6.1 贪婪匹配

默认情况下,量词( * , + , ? , {n,m} )是贪婪的,会尽可能多地匹配字符。

6.2 非贪婪匹配

在量词后面加上 ? (如 *? , +? , ?? , {n,m}? )使其变成非贪婪模式,会尽可能少地匹配字符。

案例:贪婪与非贪婪对比

import re
# {DIV_A{DIV_AA}}{DIV_B}
text = "<div>DIV_A<div>DIV_AA</div></div><div>DIV_B</div>"
pattern = r'<div>(.*)</div>'
match = re.search(pattern, text)
print("贪婪的结果:", match.group())
print("贪婪的结果", re.findall(pattern, text))
pattern = r'<div>(.*?)</div>'
match = re.search(pattern, text)
print("非贪婪的结果:", match.group())
print("非贪婪的结果:", re.findall(pattern, text))
# 一般建议对于XML/HTML格式的数据进行XPath解析

拓展:异常数据处理、递归匹配处理、性能优化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值