正则表达式从入门到精通完整教程:文本处理的利器
我以前看正则表达式就像看天书,一堆符号不知道什么意思。每次要验证邮箱、提取数据就搜半天,抄过来能用但不知道为什么。后来下决心系统学了一遍,发现其实没那么难,核心语法就那些。现在写正则很快,文本处理效率翻倍。今天把正则表达式从入门到实战的方法分享出来。
什么是正则表达式
正则表达式(Regular Expression,简称Regex)是一种文本匹配模式,用特殊语法描述字符串的规则。
用途:
1. 验证:邮箱格式对不对、手机号合不合法。
2. 提取:从文本中提取URL、电话号码。
3. 替换:批量替换文本中的内容。
4. 分割:按复杂规则分割字符串。
5. 搜索:在大量文本中找特定模式。
正则表达式几乎所有编程语言都支持,学会了一辈子受用。
基础语法
正则的核心是特殊字符(元字符)和普通字符的组合。
普通字符:字母、数字等,匹配自身。abc匹配abc。
元字符:有特殊含义的字符。
点号(.):匹配任意一个字符(除换行)。a.c匹配abc、a1c、a c。
星号(*):前面的出现0次或多次。ab*c匹配ac、abc、abbc。
加号(+):前面的出现1次或多次。ab+c匹配abc、abbc,不匹配ac。
问号(?):前面的出现0次或1次。colou?r匹配color和colour。
大括号({n,m}):前面的出现n到m次。a{2,4}匹配aa、aaa、aaaa。{n}恰好n次,{n,}至少n次。
方括号([]):匹配括号里任意一个字符。[abc]匹配a或b或c。[a-z]匹配任意小写字母。[0-9]匹配任意数字。[a-zA-Z0-9]匹配字母数字。
脱字符(^):在[]里表示"非"。[^abc]匹配不是a/b/c的字符。在[]外表示行首。^Hello匹配以Hello开头的行。
美元符($):表示行尾。world$匹配以world结尾的行。
反斜杠():转义元字符或表示特殊序列。匹配实际的点号,*匹配实际的星号。
竖线(|):或。cat|dog匹配cat或dog。
圆括号(()):分组。(ab)+匹配ab、abab、ababab。也用于提取捕获组。
这些是基础,记住这些已经能写很多正则了。
特殊字符序列
以反斜杠开头的特殊序列,代表某类字符:
d:数字,等价[0-9]。
D:非数字,等价[^0-9]。
w:字母数字下划线,等价[a-zA-Z0-9_]。
W:非字母数字下划线。
s:空白字符(空格、Tab、换行)。
S:非空白字符。
:单词边界。word匹配完整的word,不匹配password里的word。
B:非单词边界。
:换行符。 :Tab。 :回车。
这些用起来比方括号简洁。d+匹配数字,w+匹配单词。
量词详解
量词控制前面元素出现的次数。
*:0次或多次。等价{0,}。
+:1次或多次。等价{1,}。
?:0次或1次。等价{0,1}。
{n}:恰好n次。
{n,}:至少n次。
{n,m}:n到m次。
贪婪与非贪婪:
默认贪婪:尽可能多匹配。a.*b对"aabbb"匹配"aabbb"(从第一个a到最后一个b)。
非贪婪(加?):尽可能少匹配。a.*?b对"aabbb"匹配"aab"(从第一个a到第一个b)。
*?、+?、??、{n,m}?都是非贪婪版本。
我提取HTML标签内容时用非贪婪,不然会匹配过多。
分组与捕获
圆括号不只分组,还能捕获匹配内容。
(abc):捕获组,匹配abc并保存。可以用$1(或1)引用第一个捕获组。
例子:把日期2026-09-14改成14/09/2026。正则(d{4})-(d{2})-(d{2})替换为$3/$2/$1。$1是年份,$2是月份,$3是日期。
(?:abc):非捕获组,只分组不捕获。不占用$编号,性能稍好。
(?P<name>abc):命名捕获组(Python语法)。用名字引用,比数字清晰。
分组嵌套:外到内、左到右编号。(a(b(c))),$1是abc,$2是bc,$3是c。
我用捕获组做数据提取和替换,很方便。
零宽断言
断言(Lookaround)匹配位置但不消费字符,像条件判断。
正向先行断言(?=...):后面跟着什么。abc(?=def)匹配abc,但abc后面必须是def。
负向先行断言(?!...):后面不跟着什么。abc(?!def)匹配abc,但abc后面不能是def。
正向后行断言(?<=...):前面是什么。(?<=abc)def匹配def,但def前面必须是abc。
负向后行断言(?<!...):前面不是什么。(?<!abc)def匹配def,但def前面不能是abc。
断言不消耗字符,匹配的是位置。我提取价格数字时用(?<=¥)d+,匹配¥后面的数字但不包括¥。
标志位
正则后面的标志位控制匹配方式:
g:全局匹配,找所有匹配,不只是第一个。
i:忽略大小写。
m:多行模式,^和$匹配每行的行首行尾。
s:单行模式,.也匹配换行符。
u:Unicode模式,正确处理中文等。
y:粘连模式,从lastIndex开始匹配。
JavaScript里/abc/gi,Python里re.findall(pattern, string, re.I)。
常用正则模式
收集一些常用的:
邮箱:^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$
手机号(中国):^1[3-9]d{9}$
身份证号(18位):^d{17}[dXx]$
URL:^https?://[w.-]+(.[w.-]+)+.*$
IP地址:^(d{1,3}.){3}d{1,3}$(简单版)
日期(YYYY-MM-DD):^d{4}-d{2}-d{2}$
时间(HH:MM:SS):^([01]d|2[0-3]):[0-5]d:[0-5]d$
邮编:^d{6}$
QQ号:^[1-9]d{4,10}$
密码强度(8位以上含大小写数字特殊字符):^(?=.*[a-z])(?=.*[A-Z])(?=.*d)(?=.*[@$!%*?&]).{8,}$
中文字符:[u4e00-u9fa5]
HTML标签:<[^>]+>
这些模式存起来,需要直接用。但要根据实际需求调整,别盲目抄。
实战案例
案例一:验证表单输入
用户注册时验证邮箱、手机号、密码。用正则一行搞定。
JavaScript:
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/;
if (emailRegex.test(email)) { // 合法 }
Python:
import re
if re.match(r"^1[3-9]d{9}$", phone):
print("合法")
案例二:从文本提取数据
从一段文本提取所有URL:
Python:urls = re.findall(r"https?://[w./-]+", text)
JavaScript:const urls = text.match(/https?://[w./-]+/g)
提取所有邮箱:
emails = re.findall(r"[w.+-]+@[w-]+.[w.]+", text)
案例三:批量替换
把文本中的手机号中间4位打码:138****8888。
Python:re.sub(r"(1[3-9]d)d{4}(d{4})", r"1****2", text)
JavaScript:text.replace(/(1[3-9]d)d{4}(d{4})/g, "$1****$2")
把HTML标签去掉:
text.replace(/<[^>]+>/g, "")
案例四:日志分析
从Nginx日志提取IP和访问路径:
日志格式:192.168.1.1 - - [10/Sep/2026:09:00:00] "GET /index.html HTTP/1.1" 200
正则:^(d+.d+.d+.d+).*"(GET|POST) (S+)"
$1是IP,$2是方法,$3是路径。
案例五:数据清洗
提取CSV中的引号包裹字段:
"name","age","city"
正则:"([^"]*)"提取引号内的内容。
我用正则做数据清洗,省了大量手动处理时间。
正则测试工具
写正则别在脑子里想,用工具测试。
1. regex101.com:最强的在线正则测试工具,支持多种语言,有解释。我常用。
2. regexr.com:另一个在线工具。
3. VS Code搜索支持正则(Alt+R或点.*图标)。
4. 各语言的正则模块:Python的re、JavaScript的RegExp。
我写正则的流程:在regex101上测试通过,再写到代码里。
各语言正则差异
不同语言正则语法略有差异:
JavaScript:/pattern/flags或new RegExp()。replace用$1引用组。
Python:re模块。re.match(从头)、re.search(任意位置)、re.findall(找所有)、re.sub(替换)、re.split(分割)。组引用1或g<name>。
Java:java.util.regex包。Pattern和Matcher。组引用$1。
PHP:preg_match、preg_replace。组引用$1或1。
Go:regexp包。反引号正则字符串。
grep:命令行正则搜索。grep -E "pattern" file。扩展正则。
各语言核心语法一样,细节有差异,查文档确认。
性能优化
正则写不好会慢,甚至导致灾难性回溯(ReDoS)。
优化技巧:
1. 避免嵌套量词:(a+)+这种容易回溯爆炸。
2. 用具体字符代替.:用[^"\]代替.*匹配字符串内容。
3. 用非捕获组(?:):不捕获不用的组。
4. 锚定^$:减少匹配范围。
5. 避免回溯:用原子组(?>...)或占有量词?+(部分语言支持)。
6. 编译正则:循环里用预编译的正则,别每次重新编译。Python用re.compile。
7. 能用字符串方法就用字符串方法:startsWith、includes比正则快。
学习建议
正则学习路径:
1. 先学基础语法:. * + ? [] ^ $ d w s。
2. 学量词和分组:{} ()。
3. 学断言:?= ?! ?<= ?<!
4. 在regex101练习。
5. 收集常用模式。
6. 实战中用,用多了就会了。
别想一次全记住,用的时候查,用多了自然记住。我用了两年,常用语法都在脑子里了。
常见坑
1. 贪婪匹配:.*会匹配过多。用.*?非贪婪。
2. 忘记转义:匹配实际的.要写.,匹配?要写?。
3. 锚定忘记:不加^$可能部分匹配。"123"用d+会匹配,但"abc123"也会匹配里面的123。要验证整个字符串用^d+$。
4. 中文匹配:w不匹配中文,用[u4e00-u9fa5]或u标志。
5. 转义在字符串里:Python r"d"和"d"不同,r""原始字符串不用双重转义。JavaScript字符串里d要写成\d。
我的正则使用习惯
1. 先在regex101测试,再写代码。
2. 加注释说明正则含义(复杂正则可读性差)。
3. 收集常用模式存笔记。
4. 性能敏感场景测试正则速度。
5. 能用字符串方法就不滥用正则。
正则用好了是利器,用不好是灾难。多练多用,自然熟练。
总结
正则表达式看着吓人,其实核心语法不多。基础语法+量词+分组+断言,掌握这些日常够用。关键是多练多用,在regex101上测试,实战中积累。学会正则,文本处理效率翻倍,验证、提取、替换一招搞定。这是程序员受益一生的技能。
