正则表达式从入门到精通教程
这个教程会通过三个阶段(入门、进阶、精通)带你逐步学习正则表达式,附带实战练习和如何逐步编写符合需求的正则表达式。
阶段 1:入门
1. 什么是正则表达式?
正则表达式(Regular Expression, regex)是一种用于字符串匹配和处理的模式描述语言,可以用来搜索、替换和验证字符串。
2. 必备基础概念
特殊符号
.
: 匹配任意单个字符(除换行符)。- 示例:
a.c
匹配abc
,a1c
等。
- 示例:
[]
: 匹配字符集中的任意一个字符。- 示例:
[abc]
匹配a
,b
,c
。
- 示例:
-
: 表示字符范围。- 示例:
[a-z]
匹配所有小写字母。
- 示例:
\
: 转义符,用于匹配特殊字符本身。- 示例:
\.
匹配点号.
。
- 示例:
量词
*
: 匹配前面的内容 0 次或多次。- 示例:
a*
匹配""
,a
,aaa
。
- 示例:
+
: 匹配前面的内容 1 次或多次。- 示例:
a+
匹配a
,aa
。
- 示例:
?
: 匹配前面的内容 0 次或 1 次。- 示例:
a?
匹配""
,a
。
- 示例:
{n}
: 匹配前面的内容正好 n 次。- 示例:
a{3}
匹配aaa
。
- 示例:
3. 入门实战:用正则匹配简单的格式
任务:匹配一个简单的电话号码格式
- 示例号码:
123-456-7890
- 步骤:
- 分析结构:由数字、短横线组成,格式是
三位数字-三位数字-四位数字
。 - 转换为正则:
\d{3}-\d{3}-\d{4}
。
- 分析结构:由数字、短横线组成,格式是
python
import re
pattern = r"\d{3}-\d{3}-\d{4}"
text = "我的电话是 123-456-7890,请联系我。"
match = re.search(pattern, text)
if match:
print("找到电话号码:", match.group())
阶段 2:进阶
1. 边界匹配
^
: 匹配字符串的开头。- 示例:
^hello
匹配以hello
开头的字符串。
- 示例:
$
: 匹配字符串的结尾。- 示例:
world$
匹配以world
结尾的字符串。
- 示例:
任务:验证字符串是否是邮箱格式
- 示例邮箱:
example@mail.com
- 分析结构:
- 开头是字母或数字,可能包含下划线、点号。
- 接着是
@
,后面是域名。 - 域名部分由字母数字组成,最后是点加后缀。
- 转换为正则:
^\w+@\w+\.\w+$
python
pattern = r"^\w+@\w+\.\w+$"
email = "example@mail.com"
if re.match(pattern, email):
print("合法邮箱")
else:
print("非法邮箱")
2. 分组与引用
分组用 ()
表示,引用用 \数字
指代之前的分组。
任务:匹配重复的单词
- 示例文本:
hello hello world
- 分析需求:
- 匹配相邻两个完全相同的单词。
- 转换为正则:
\b(\w+)\s+\1\b
python
pattern = r"\b(\w+)\s+\1\b"
text = "hello hello world"
match = re.search(pattern, text)
if match:
print("找到重复单词:", match.group())
3. 实战练习:提取日期
- 示例日期:
2024-11-17
- 分析需求:
- 格式是
年-月-日
。 - 年是四位数字,月和日是两位数字。
- 转换为正则:
(\d{4})-(\d{2})-(\d{2})
- 格式是
python
pattern = r"(\d{4})-(\d{2})-(\d{2})"
date = "今天是 2024-11-17。"
match = re.search(pattern, date)
if match:
year, month, day = match.groups()
print(f"提取日期: 年={year}, 月={month}, 日={day}")
阶段 3:精通
1. 贪婪与懒惰模式
- 默认是贪婪模式:尽可能多地匹配。
- 加
?
转为懒惰模式:尽可能少地匹配。
任务:匹配 HTML 标签内容
- 示例文本:
<div>Hello</div><div>World</div>
- 贪婪模式:
<.*>
会匹配整个<div>Hello</div><div>World</div>
。 - 懒惰模式:
<.*?>
会分别匹配<div>
和</div>
。
python
pattern = r"<.*?>"
text = "<div>Hello</div><div>World</div>"
matches = re.findall(pattern, text)
print("匹配结果:", matches)
2. 高级应用:嵌套匹配
任务:匹配嵌套结构(如数学表达式)
正则表达式不能直接处理嵌套结构,但可以通过多步操作实现。
- 示例文本:
(a + (b - c))
- 分析需求:
- 提取匹配外层和内层括号。
- 转换为正则:
\([^()]*\)
python
pattern = r"\([^()]*\)"
text = "(a + (b - c)) + (d / e)"
matches = re.findall(pattern, text)
print("匹配括号内容:", matches)
3. 动态构造正则表达式
使用 re.compile
动态创建正则表达式,支持多种标志。
python
import re
flags = re.IGNORECASE | re.MULTILINE
pattern = re.compile(r"hello", flags)
text = "Hello world\nhello again"
matches = pattern.findall(text)
print("匹配结果:", matches)
总结:如何编写正则表达式?
- 分析目标结构:
- 定位模式中的固定部分和可变部分。
- 确定字符类型(数字、字母、特殊字符)。
- 逐步分解编写:
- 从简单匹配入手,逐步增加复杂逻辑。
- 测试表达式:
- 使用在线工具(如 Regex101)。
- 优化与调试:
- 使用非贪婪模式或分组引用优化结果。
附:正则表达式一览表
1. 元字符(Metacharacters)
元字符是正则表达式的核心,用于匹配特定类型的字符或定义模式。
基本元字符:
元字符 | 作用 | 示例 | 匹配结果 |
---|---|---|---|
. |
匹配任意单个字符(除了换行符) | a.c |
匹配 abc , a1c 等 |
\ |
转义符,用于匹配特殊字符本身 | a\.c |
匹配 a.c |
^ |
匹配字符串的开头 | ^abc |
匹配以 abc 开头的字符串 |
$ |
匹配字符串的结尾 | xyz$ |
匹配以 xyz 结尾的字符串 |
* |
匹配前一个字符 0 次或多次 | a* |
匹配 "" , a , aaa 等 |
+ |
匹配前一个字符 1 次或多次 | a+ |
匹配 a , aa 等,但不匹配 "" |
? |
匹配前一个字符 0 次或 1 次 | a? |
匹配 "" , a |
{n} |
匹配前一个字符正好 n 次 | a{3} |
匹配 aaa |
{n,} |
匹配前一个字符至少 n 次 | a{2,} |
匹配 aa , aaa 等 |
{n,m} |
匹配前一个字符至少 n 次,至多 m 次 | a{2,4} |
匹配 aa , aaa , aaaa |
字符集和范围:
元字符 | 作用 | 示例 | 匹配结果 |
---|---|---|---|
[] |
匹配括号内任意字符 | [abc] |
匹配 a , b , c |
[^] |
匹配不在括号内的字符 | [^abc] |
匹配除 a , b , c 以外的字符 |
- |
指定字符范围 | [a-z] |
匹配 a 到 z 的任意小写字母 |
预定义字符集:
表达式 | 作用 | 示例 | 匹配结果 |
---|---|---|---|
\d |
匹配任意数字(0-9) | \d+ |
匹配 123 , 4 等 |
\D |
匹配任意非数字 | \D+ |
匹配 abc , !@# 等 |
\w |
匹配任意单词字符(字母、数字或下划线) | \w+ |
匹配 abc , a1_ 等 |
\W |
匹配任意非单词字符 | \W+ |
匹配 @#$ 等 |
\s |
匹配任意空白字符(空格、制表符等) | \s+ |
匹配空格、换行符等 |
\S |
匹配任意非空白字符 | \S+ |
匹配 abc , 123 等 |
2. 匹配模式(Greedy vs Lazy)
正则表达式默认是贪婪模式(尽可能多地匹配),但可以通过 ?
使其变为非贪婪模式(尽可能少地匹配)。
表达式 | 作用 | 示例 | 匹配结果 |
---|---|---|---|
.* |
贪婪:匹配尽可能多的字符 | <.*> |
匹配整个 <html><body> |
.*? |
非贪婪:匹配尽可能少的字符 | <.*?> |
分别匹配 <html> 和 <body> |
3. 分组与引用
表达式 | 作用 | 示例 | 匹配结果 |
---|---|---|---|
() |
定义一个组 | (abc)+ |
匹配 abc , abcabc 等 |
(?:...) |
非捕获组,不保存匹配结果 | (?:abc)+ |
匹配但不保存组 |
\n |
引用第 n 个捕获组 | (a)(b)\1\2 |
匹配 abab |
4. 边界匹配
表达式 | 作用 | 示例 | 匹配结果 |
---|---|---|---|
\b |
匹配单词边界 | \bword\b |
匹配 word ,但不匹配 sword |
\B |
匹配非单词边界 | \Bword\B |
匹配 sworded 中的 word |
5. 标志(Flags)
标志修改正则表达式的行为:
标志 | 作用 | 示例 |
---|---|---|
re.IGNORECASE / re.I |
忽略大小写 | re.match("abc", "ABC", re.I) |
re.DOTALL / re.S |
让 . 匹配换行符 |
re.match("a.b", "a\nb", re.S) |
re.MULTILINE / re.M |
多行模式,^ 和 $ 匹配每一行 |
re.match("^abc$", "abc\ndef", re.M) |
6. 常见正则表达式例子
表达式 | 匹配内容 |
---|---|
^\d{4}-\d{2}-\d{2}$ |
日期格式:YYYY-MM-DD |
\w+@\w+\.\w+ |
电子邮件地址 |
https?://\S+ |
匹配 URL |
\d{3}-\d{3}-\d{4} |
电话号码:123-456-7890 |