news 2026/7/23 5:13:14

文本的瑞士军刀:Python正则表达式完全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
文本的瑞士军刀:Python正则表达式完全解析

🔎大家好,我是ZTLJQ,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流

📝个人主页-ZTLJQ的主页

🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​📣系列果你对这个系列感兴趣的话

专栏 - ​​​​​​Python从零到企业级应用:短时间成为市场抢手的程序员

✔说明⇢本人讲解主要包括Python爬虫、JS逆向、Python的企业级应用

如果你对这个系列感兴趣的话,可以关注订阅哟👋

为什么需要正则表达式?

想象一下,你需要完成以下任务:

  • 验证用户输入的电话号码格式是否正确。
  • 从一篇包含成百上千个单词的文章中,找出所有以“http”或“https”开头的网址链接。
  • 将一段文本中所有的日期格式从MM/DD/YYYY统一转换为YYYY-MM-DD
  • 清理日志文件,移除其中所有的IP地址。

使用传统的字符串方法(如find(),split(),replace())来实现这些需求,代码会变得极其复杂和脆弱。例如,要找网址,你可能需要写一个循环,检查每个子串是否以“http”开头,然后找到下一个空格...

正则表达式(Regex)提供了一种强大的“模式”语言。你可以用一个紧凑的字符串(称为“模式”)来描述你想要匹配的文本规则,然后让引擎去自动搜索、替换或分割文本。

Python通过内置的re模块提供了完整的正则表达式支持。


第一部分:核心概念与基本语法
1.1 基础元字符
元字符含义示例
.匹配除换行符外的任意单个字符a.c匹配 "abc", "a2c", "a c"
^匹配字符串的开始^Hello匹配 "Hello World" 的开头
$匹配字符串的结束world$匹配 "Hello world" 的结尾
*匹配前面的字符零次或多次ab*c匹配 "ac", "abc", "abbc", "abbbc"...
+匹配前面的字符一次或多次ab+c匹配 "abc", "abbc",但不匹配 "ac"
?匹配前面的字符零次或一次ab?c匹配 "ac", "abc",但不匹配 "abbc"
{m,n}匹配前面的字符m到n次a{2,4}匹配 "aa", "aaa", "aaaa"
\转义字符,将特殊字符变为普通字符\.匹配字面量的点号 "."
1.2 字符集 ([]) 与预定义字符类
  • 字符集[...]: 匹配方括号内的任意一个字符。

    • [aeiou]: 匹配任何一个元音字母。
    • [a-z]: 匹配任何一个小写字母。
    • [0-9A-F]: 匹配任何十六进制数字。
    • [^0-9]:^在开头表示否定,匹配任何非数字字符。
  • 预定义字符类 (更简洁):

    • \d: 等价于[0-9],匹配任何数字。
    • \D: 等价于[^0-9],匹配任何非数字。
    • \w: 匹配任何“单词字符”,等价于[a-zA-Z0-9_]
    • \W: 匹配任何“非单词字符”。
    • \s: 匹配任何空白字符(空格、制表符\t、换行符\n等)。
    • \S: 匹配任何非空白字符。
1.3 分组 (()) 与捕获

圆括号()用于创建分组,可以对分组应用量词,并且可以“捕获”匹配到的文本以便后续使用。

import re # 使用分组和量词 pattern = r"(ab)+" # 匹配一个或多个连续的 "ab" text = "ababab cd ab" match = re.search(pattern, text) if match: print(f"找到: {match.group()}") # 找到: ababab # group(0) 或 group() 是整个匹配 # group(1) 是第一个分组的内容 # 注意: 这里只有一个分组 (ab),所以 group(1) 返回最后一次成功的捕获 "ab"

注意:re.search()只返回第一个匹配项。re.findall()返回所有匹配项的列表。


第二部分:re模块实战案例

我们将通过一系列实际案例来展示re模块的强大功能。

案例一:验证与匹配 (re.match,re.search,re.findall)
import re # ---- 验证邮箱地址 ---- def is_valid_email(email): """ 一个简化的邮箱验证模式。 注意:真正的邮箱验证非常复杂,这里仅作演示。 """ pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' return re.match(pattern, email) is not None print(is_valid_email("user@example.com")) # True print(is_valid_email("invalid.email")) # False # ---- 从文本中提取所有邮箱 ---- text = """ Contact us at support@company.com or sales@company.org. For jobs, email hr@company.io. """ # findall 返回所有匹配的字符串 emails = re.findall(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}', text) print(emails) # ['support@company.com', 'sales@company.org', 'hr@company.io'] # ---- 查找所有以 http/https 开头的URL ---- web_text = "Visit https://www.python.org or http://example.com for more info." urls = re.findall(r'https?://[^\s]+', web_text) # \S+ 匹配非空白字符直到遇到空白 print(urls) # ['https://www.python.org', 'http://example.com']

解析:

  • r'': 使用原始字符串(raw string),避免反斜杠被Python解释器转义。
  • https?:?表示s是可选的,因此能匹配"http"和"https"。
  • [^\s]+:[^...]表示否定字符集,[^\s]匹配任何非空白字符,+表示一个或多个。
案例二:搜索与替换 (re.sub)

这是最常用的功能之一。

import re # ---- 格式化日期:将 MM/DD/YYYY 转换为 YYYY-MM-DD ---- date_text = "The event is on 12/25/2023 and 01/01/2024." # 使用分组捕获 # (\d{2}) 捕获月份,(\d{2}) 捕获日期,(\d{4}) 捕获年份 pattern = r'(\d{2})/(\d{2})/(\d{4})' replacement = r'\3-\1-\2' # \3是年份,\1是月份,\2是日期 formatted_text = re.sub(pattern, replacement, date_text) print(formatted_text) # The event is on 2023-12-25 and 2024-01-01. # ---- 移除文本中的所有HTML标签 ---- html_text = "<p>This is <b>bold</b> and <i>italic</i> text.</p>" # <[^>]+> 匹配一个左尖括号<,然后是非>的任意字符一个或多次,最后是一个右尖括号> clean_text = re.sub(r'<[^>]+>', '', html_text) print(clean_text) # This is bold and italic text. # ---- 将多个连续空格替换为单个空格 ---- messy_text = "Too many spaces here." cleaned_spaces = re.sub(r'\s+', ' ', messy_text).strip() print(cleaned_spaces) # Too many spaces here.

解析:re.sub(pattern, replacement, string)会将string中所有匹配pattern的部分替换为replacement。在replacement中,可以用\1,\2...来引用前面分组捕获的内容。

案例三:分割字符串 (re.split)

str.split()更强大,可以使用复杂的模式作为分隔符。

import re # ---- 使用多种分隔符分割 ---- text = "apple,banana;cherry|date" # [,;|] 是一个字符集,匹配 , 或 ; 或 | fruits = re.split(r'[,;|]', text) print(fruits) # ['apple', 'banana', 'cherry', 'date'] # ---- 分割句子,保留标点符号 ---- sentence = "Hello world! How are you? I'm fine." # (?<=[.!?]) 是一个“后行断言”,意思是“在 . 或 ! 或 ? 之后” # 这样分割时,分隔符(标点)会被保留在前面的元素中 parts = re.split(r'(?<=[.!?])\s*', sentence) print(parts) # ['Hello world!', "How are you?", "I'm fine."]

解析:(?<=...)是“正向后行断言”(positive lookbehind assertion)。它确保了分割只发生在句号、感叹号或问号之后的空白处,而不会吃掉这些标点符号。

第三部分:高级特性与技巧
3.1 编译模式 (re.compile)

如果你要重复使用同一个正则表达式,将其编译成一个Pattern对象可以提高效率。

import re # 编译一个模式,用于匹配有效的IPv4地址段(简化版) ip_pattern = re.compile(r'\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b') text_with_ips = "Server1: 192.168.1.1, Server2: 10.0.0.5, Invalid: 999.999.999.999" # 多次使用编译后的模式 valid_ips = [] for ip in ip_pattern.findall(text_with_ips): # 进一步验证每个数字是否在0-255之间 if all(0 <= int(part) <= 255 for part in ip.split('.')): valid_ips.append(ip) print(valid_ips) # ['192.168.1.1', '10.0.0.5']

解析:re.compile()返回一个Pattern对象,它拥有search(),findall(),sub()等方法。

3.2 标志 (Flags)

可以改变正则表达式的匹配行为。

import re text = "Hello\nWorld\nHELLO" # ---- 忽略大小写 (re.IGNORECASE 或 re.I) ---- matches = re.findall(r'hello', text, re.IGNORECASE) print(matches) # ['Hello', 'HELLO'] # ---- 让 ^ 和 $ 匹配每一行的开始和结束 (re.MULTILINE 或 re.M) ---- # 默认情况下,^ 和 $ 只匹配整个字符串的开始和结束 line_matches = re.findall(r'^\w+', text, re.MULTILINE) # 找出每行的第一个单词 print(line_matches) # ['Hello', 'World', 'HELLO'] # ---- 让 . 匹配包括换行符在内的所有字符 (re.DOTALL 或 re.S) ---- multiline_text = "Line 1\nLine 2\nLine 3" # .* 默认不能跨行 single_line_match = re.search(r'Line 1.*Line 3', multiline_text) # None # 使用 re.DOTALL multi_line_match = re.search(r'Line 1.*Line 3', multiline_text, re.DOTALL) if multi_line_match: print("Found across lines!") # Found across lines!
3.3 非贪婪匹配

默认情况下,*,+,?,{m,n}是“贪婪”的,它们会尽可能多地匹配字符。

import re text = "<div>Content 1</div><div>Content 2</div>" # 贪婪匹配:会匹配从第一个 <div> 到最后一个 </div> 的所有内容 greedy_pattern = r'<div>.*</div>' greedy_match = re.search(greedy_pattern, text) if greedy_match: print("Greedy:", greedy_match.group()) # Greedy: <div>Content 1</div><div>Content 2</div> # 非贪婪匹配:在量词后加 ?,会尽可能少地匹配 non_greedy_pattern = r'<div>.*?</div>' non_greedy_matches = re.findall(non_greedy_pattern, text) print("Non-greedy:", non_greedy_matches) # Non-greedy: ['<div>Content 1</div>', '<div>Content 2</div>']

解析:.*?中的?*变成非贪婪(或“懒惰”)模式。它一旦找到第一个</div>就停止匹配,而不是继续寻找最后一个。

第四部分:最佳实践与陷阱
  1. 保持简单: 正则表达式很容易变得过于复杂而难以维护。如果一个简单的str方法就能解决,就不要用正则。
  2. 充分测试: 正则表达式是“写时容易,读时难”。务必用各种边界情况测试你的模式。
  3. 使用原始字符串: 总是在正则表达式前加上r,如r'\d+',避免反斜杠问题。
  4. 善用在线工具: 使用 regex101.com 或 pythex.org 等在线工具来编写和调试正则表达式,它们提供实时的匹配结果和解释。
  5. 性能考量: 复杂的正则表达式,尤其是涉及大量回溯(backtracking)的,可能会很慢。对于简单的查找,instr.find()通常更快。
  6. 不要过度依赖: 试图用一个正则表达式解析HTML或JSON通常是错误的选择。应使用专门的解析器(如BeautifulSoupjson模块)。
结语

正则表达式是一把双刃剑。它功能强大,能让你在几行代码内完成复杂的文本处理任务;但它也晦涩难懂,写出的“天书”会让未来的自己都头疼。

通过本篇博客的学习,你应该已经掌握了:

  • 正则表达式的核心语法和元字符。
  • 如何使用re模块进行搜索、匹配、替换和分割。
  • 编译模式、标志和非贪婪匹配等高级技巧。
  • 实际应用场景和最佳实践。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 14:18:25

OFA视觉问答模型部署教程:GPU算力适配+显存优化说明

OFA视觉问答模型部署教程&#xff1a;GPU算力适配显存优化说明 1. 引言&#xff1a;让AI看懂图片并回答问题 你有没有想过&#xff0c;给AI看一张照片&#xff0c;然后直接问它“图片里有什么&#xff1f;”或者“那个东西是什么颜色的&#xff1f;”&#xff0c;它就能像人一…

作者头像 李华
网站建设 2026/7/14 14:18:24

const关键字

讲解两个小点&#xff1a;scanf和scanf_s的区别&#xff0c;字符串输入scanf是C语言的标准输入输出,scanf_s是微软为C语言提供的安全增强版本两种用法&#xff1a;#include <stdio.h>int main() {// 1. 读取数值类型&#xff08;int/float/double&#xff09;int num;pri…

作者头像 李华
网站建设 2026/7/14 14:18:27

互联网高并发场景:MogFace-large在社交平台图片审核中的落地实践

互联网高并发场景&#xff1a;MogFace-large在社交平台图片审核中的落地实践 1. 引言 想象一下&#xff0c;一个大型社交平台&#xff0c;每天有数千万甚至上亿张图片被用户上传。这些图片里&#xff0c;有自拍、有风景、有宠物&#xff0c;当然&#xff0c;也可能混杂着一些…

作者头像 李华
网站建设 2026/7/14 14:18:29

基于Transformer架构的LiuJuan20260223Zimage模型深度解析与性能调优

基于Transformer架构的LiuJuan20260223Zimage模型深度解析与性能调优 最近在图像生成领域&#xff0c;一个名为LiuJuan20260223Zimage的模型开始引起不少开发者的注意。它不像那些耳熟能详的通用大模型&#xff0c;而是专注于图像生成任务&#xff0c;并且在一些特定场景下表现…

作者头像 李华
网站建设 2026/7/14 14:18:40

Z-Image-Turbo孙珍妮LoRA镜像实战教程:Xinference+Gradio一键部署保姆级指南

Z-Image-Turbo孙珍妮LoRA镜像实战教程&#xff1a;XinferenceGradio一键部署保姆级指南 1. 教程概述 今天给大家带来一个特别实用的教程——如何使用Z-Image-Turbo孙珍妮LoRA镜像快速搭建个人专属的文生图服务。这个镜像基于先进的Z-Image-Turbo模型&#xff0c;专门针对生成…

作者头像 李华