正则表达式 – 示例(保姆级教程)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
前言
在编程和数据处理中,正则表达式(Regular Expression)是一种强大的文本匹配工具,被广泛应用于字符串搜索、验证、替换等场景。对于编程初学者而言,它可能显得抽象难懂;而对中级开发者来说,深入掌握其核心语法和高级技巧可以显著提升开发效率。本文通过循序渐进的方式,结合具体示例,帮助读者理解正则表达式的基本原理和实际应用。
一、正则表达式的基础语法
1.1 什么是正则表达式?
正则表达式可以被理解为一种“通缉令”,它通过特定的符号组合,描述需要匹配的文本模式。例如,如果你想找到所有以 @
符号开头、以 .com
结尾的字符串,正则表达式能快速完成这一任务。
1.2 基础元素:字符与字面量
正则表达式中最简单的模式是直接匹配字符。例如:
hello
匹配字符串中的“hello”;123
匹配数字序列“123”。
但有些字符具有特殊含义,称为元字符(如 .
、*
、+
),需要通过转义符 \
来表示其字面意义。例如:
import re
pattern = r"\." # 匹配实际的点字符"."
text = "example.com"
matches = re.findall(pattern, text)
print(matches) # 输出:["."]
1.3 元字符的初步应用:.
和 *
.
(点号):匹配除换行符外的任意单个字符。
例如,a.b
可以匹配a1b
、a b
、aXb
,但不能匹配aab
。*
(星号):匹配前一个字符的零次或多次出现。
例如,a*b
匹配b
(零次a
)、ab
(一次a
)、aaab
(多次a
)。
示例代码(JavaScript):
const str = "aabbb";
const regex = /a*b/;
console.log(regex.test(str)); // 输出:true(匹配前两个 a 后的 b)
二、元字符与特殊符号的进阶用法
2.1 字符集与字符范围:[]
和 [^]
- 字符集
[]
:匹配括号内的任意一个字符。例如,[abc]
匹配a
、b
或c
。pattern = r"[0-9]" # 匹配任意数字 text = "price: $12.99" matches = re.findall(pattern, text) print(matches) # 输出:['1', '2', '9', '9']
- 否定字符集
[^]
:匹配不在括号内的任意字符。例如,[^0-9]
匹配非数字字符。
2.2 量词的扩展:+
、?
和 {n,m}
+
:匹配前一个字符的一次或多次出现。
例如,a+b
匹配ab
、aaab
,但不匹配b
。?
:匹配前一个字符的零次或一次出现。
例如,colou?r
匹配color
(美式拼写)或colour
(英式拼写)。{n,m}
:匹配前一个字符的最少n
次、最多m
次出现。
例如,a{2,4}b
匹配aab
、aaab
、aaaab
。
2.3 锚点:^
和 $
^
:匹配字符串的开头。例如,^hello
只匹配以hello
开头的字符串。$
:匹配字符串的结尾。例如,\.com$
只匹配以.com
结尾的字符串。
案例:邮箱验证
// 匹配简单邮箱格式:以字母开头,包含 @ 符号,以 .com 结尾
const emailRegex = /^[a-zA-Z0-9]+@[a-zA-Z0-9]+\.(com|net)$/;
console.log(emailRegex.test("test@example.com")); // true
console.log(emailRegex.test("test@domain")); // false(缺少后缀)
三、常见正则表达式模式与实际案例
3.1 电话号码验证
假设需要验证中国手机号码(11 位数字,以 13
、15
、18
等开头):
phone_pattern = r"^1[3-9]\d{9}$"
text = "13812345678"
print(re.fullmatch(phone_pattern, text)) # 匹配成功
3.2 URL 解析
提取 URL 中的协议和路径:
const url = "https://example.com/path/to/page?query=123";
const regex = /^(\w+):\/\/([^\/]+)(.*)/;
const matches = url.match(regex);
console.log(matches);
// 输出:["https://example.com/path/to/page", "https", "example.com", "/path/to/page"]
- 解释:
(\w+)
捕获协议(如http
、https
);([^\/]+)
捕获域名;(.*)
捕获路径和查询参数。
3.3 文本替换:删除 HTML 标签
使用正则表达式去除字符串中的 HTML 标签:
text = "<p>这是段落。</p><div>内容</div>";
clean_text = re.sub(r"<.*?>", "", text);
print(clean_text); # 输出:这是段落。内容
注意:此示例为简化版,实际处理复杂 HTML 需使用专用库(如 BeautifulSoup)。
四、高级技巧与常见陷阱
4.1 分组与捕获
使用 ()
对匹配结果进行分组,便于后续引用。例如,提取邮箱中的用户名和域名:
const email = "user@example.com";
const regex = /(.+)@(.+)/;
const [, username, domain] = email.match(regex);
console.log(username); // user
console.log(domain); // example.com
4.2 非贪婪匹配:*?
、+?
默认量词是“贪婪的”,会尽可能多匹配字符。添加 ?
可以改为非贪婪模式:
text = "<div>内容1</div><div>内容2</div>";
greedy = re.search(r"<div>(.*?)</div>", text);
print(greedy.group(1)); # 输出:内容1</div><div>内容2
non_greedy = re.search(r"<div>(.*?)</div>", text, re.DOTALL);
print(non_greedy.group(1)); # 输出:内容1
4.3 回溯与性能优化
复杂的正则表达式可能导致“回溯爆炸”(Backtracking Catastrophe),显著降低性能。例如,模式 .*a.*b
在处理长字符串时可能因多次回溯而崩溃。
解决方案:
- 使用非捕获组
(?:...)
减少分组; - 避免不必要的重复模式。
4.4 跨语言兼容性
不同编程语言对正则表达式的实现略有差异。例如:
- Python 中需使用
re
模块; - JavaScript 直接支持正则表达式字面量
/pattern/
; - Java 需通过
Pattern
和Matcher
类操作。
案例:密码强度验证
// 验证密码:至少 8 位,含大写字母、小写字母、数字
const passwordRegex = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).{8,}$/;
console.log(passwordRegex.test("Pass1234")); // true
console.log(passwordRegex.test("pass")); // false
五、结论
正则表达式是一门需要“边学边练”的技术。通过理解基础语法、掌握元字符的含义、结合实际案例练习,开发者可以逐步提升其使用能力。无论是验证用户输入、解析日志文件,还是自动化文本处理,正则表达式都能提供高效且灵活的解决方案。
关键点回顾:
- 字符、元字符与量词的组合构成正则表达式的核心;
- 锚点和分组帮助精准定位目标文本;
- 避免陷阱(如贪婪匹配)是优化性能的关键。
通过持续实践和查阅文档,正则表达式将成为你工具箱中不可或缺的利器。