上一章我们讲了 JSON。
JSON 解决的是结构化数据的问题。 也就是说,数据本身就规规矩矩,层级清楚,字段明确,程序很容易读懂。
可真实开发里,还有很多数据根本没那么老实。
比如一大段聊天记录。 一份杂乱的日志文本。 一页网页源码。 一堆用户输入的内容。 一篇文章里的手机号、邮箱、日期、订单号。
这些内容往往不是整整齐齐摆在你面前的。 它们藏在一大段文本里,东一个,西一个。
这时候,如果你还靠最基础的字符串查找,很多场景就会变得很吃力。
于是,正则表达式就派上用场了。
它最擅长做的事,就是从复杂文本中,找到符合某种规则的内容。
这也是为什么很多程序员第一次接触正则时,会觉得它神奇。 因为它看起来像一串古怪符号,但一旦掌握,处理文本的效率会一下子提高很多。
一、正则表达式到底是什么
先别被名字吓到。
正则表达式,本质上就是一种文本匹配规则。
你可以把它理解成一种筛选条件。
比如你说,我想找出文本里所有数字。 这就是一个规则。
你说,我想找出所有手机号。 这也是一个规则。
你说,我想找出所有以 .jpg 结尾的文件名。 这还是一个规则。
而正则表达式,就是用一套专门的写法,把这些规则描述出来。
比如:
\d+
它表示匹配一个或多个数字。
再比如:
[a-z]+
它表示匹配一个或多个小写字母。
所以你会发现,正则不是在写程序逻辑。 它更像是在描述一种模式。
程序看到这个模式后,就能自动去文本里找符合条件的内容。
二、为什么普通字符串方法不够用
有些同学可能会问,字符串不是已经有 find()、replace()、split() 这些方法了吗,为什么还要学正则
因为普通字符串方法更适合处理固定内容。 而正则更适合处理有规律,但不固定的内容。
比如下面这段文字:
text = "请联系我,手机号是13812345678,备用号码是15988886666"
如果你只是想找某个固定号码,用 find() 当然可以。 可如果你根本不知道号码具体是多少,只知道它们都是 11 位数字,那普通方法就不够灵活了。
再比如,你要从一堆文本中提取所有邮箱地址。 邮箱前面的用户名不固定,后面的域名也不固定。 这时候你很难用死板的字符串方法一个个去找。
但正则可以直接描述规则:
一串字符 后面跟一个 @ 再后面跟域名 最后再跟后缀
这种问题,正则天生就适合。
三、先建立一个最核心的认识
学正则时,你一定要先接受一个思路:
你不是在匹配某个具体文本。 你是在匹配一类文本。
比如:
匹配 123456,这是具体内容。 匹配“任意 6 位数字”,这是规则。
正则真正强大的地方,不在于找一个确定的字符串。 而在于它能把很多相似的字符串,统一用一条规则概括出来。
所以以后你看到正则,不要问它在找哪一个词。 要问它在描述什么规律。
这个思路一旦建立起来,后面就容易多了。
四、最简单的正则,从精确匹配开始
正则并不一定一上来就是各种符号。 最简单的正则,其实就是普通文本本身。
例如:
python
它就表示匹配字符串 python。
来看例子:
import retext = "我在学 python,也在了解 python 爬虫"result = re.findall("python", text)print(result)
输出结果:
['python', 'python']
这说明,正则最基础的能力,就是按照你给出的模式去找内容。
这里只不过模式非常简单,就是一个固定单词。
所以你可以把正则学习理解成一个过程:
先从固定文本匹配开始。 再慢慢升级到灵活规则匹配。
五、点号是什么意思
正则里最常见的特殊符号之一,就是点号 .
它表示匹配任意单个字符。 注意,是任意单个字符,不是任意多个字符。
比如:
a.c
它可以匹配:
abca1ca-caxc
因为中间那个点,可以代表任意一个字符。
来看代码:
import retext = "abc a1c axc a-c ac"result = re.findall("a.c", text)print(result)
输出结果一般会是:
['abc', 'a1c', 'axc', 'a-c']
为什么 ac 没匹配上
因为 a.c 要求中间必须有一个字符。 而 ac 中间什么都没有,所以不符合规则。
这就是正则里非常典型的一种思维: 不是看着像就行,而是必须严格符合模式。
六、字符类是什么
很多时候,我们不是想匹配任意字符,而是想匹配某一类字符。
比如: 只要数字 只要字母 只要某几个指定字符
这时候,就会用到中括号 []
例如:
[abc]
表示匹配 a、b、c 中的任意一个。
import retext = "a b c d e"result = re.findall("[abc]", text)print(result)
输出:
['a', 'b', 'c']
你也可以写范围。
比如:
[0-9]
表示任意一个数字。
[a-z]
表示任意一个小写字母。
[A-Z]
表示任意一个大写字母。
[a-zA-Z0-9]
表示任意一个字母或数字。
这是正则里非常常用的一类写法。
七、几个最常见的快捷写法
为了少写一点,正则还提供了很多简写方式。
先记住最常见的这几个就够用了。
\d
表示任意一个数字,等价于 [0-9]
\D
表示任意一个非数字
\w
表示数字、字母、下划线中的任意一个
\W
表示非数字、字母、下划线
\s
表示空白字符,比如空格、换行、制表符
\S
表示非空白字符
这些写法你后面会经常见到,尤其是提取手机号、账号、编号、单词时,非常高频。
来看一个数字匹配例子:
import retext = "今天卖了12件,昨天卖了30件,上周卖了105件"result = re.findall(r"\d+", text)print(result)
输出:
['12', '30', '105']
这里的 \d+ 意思是匹配一个或多个数字。 这个 + 我们马上讲。
八、数量词才是正则真正灵活的关键
光知道字符还不够。 正则之所以强,是因为你可以控制字符出现多少次。
最常见的数量词有这几个。
*
表示前面的内容可以出现 0 次或多次
+
表示前面的内容可以出现 1 次或多次
?
表示前面的内容可以出现 0 次或 1 次
{n}
表示前面的内容必须出现 n 次
{m,n}
表示前面的内容出现 m 到 n 次
这个地方非常重要。
来看几个例子。
\d+
表示一个或多个数字
所以它能匹配:
518202599999
再比如:
a*
表示 a 可以出现 0 次或多次。
也就是说,空字符串也可能符合。
再比如:
\d{11}
表示连续 11 位数字。
这就是手机号匹配里常见的基础写法之一。
你会发现,一旦字符规则和数量规则结合起来,表达能力就一下子上来了。
九、为什么 \d 和 \d+ 差别很大
这是新手经常忽略的点。
\d 表示一个数字。\d+ 表示一串数字。
看起来只差一个加号,意思完全不同。
例如文本:
text = "订单号是12345"
如果你用:
\d
匹配到的会是:
12345
如果你用:
\d+
匹配到的会是:
12345
来看代码:
import retext = "订单号是12345"print(re.findall(r"\d", text))print(re.findall(r"\d+", text))
输出结果:
['1', '2', '3', '4', '5']['12345']
所以正则里,数量词不是装饰品。 它直接决定你匹配出来的结果到底是什么。
十、开头和结尾也可以控制
有时候我们不是想在文本里随便找一段,而是要求整个字符串必须满足某种规则。
这时候就要用到:
^
表示开头
$
表示结尾
比如:
^\d{11}$
意思是:
从开头到结尾,整个字符串必须是 11 位数字。
这就比单纯写 \d{11} 更严格。
为什么
因为只写 \d{11},只要文本里某个位置出现了 11 位数字,就可能匹配成功。 而加上开头和结尾后,就要求整个字符串从头到尾都得符合。
看例子:
import retext1 = "13812345678"text2 = "手机号是13812345678"print(re.findall(r"^\d{11}$", text1))print(re.findall(r"^\d{11}$", text2))
输出大致是:
['13812345678'][]
这类写法很常用于校验。
比如校验手机号、身份证号、用户名格式、密码规则等。
十一、竖线表示或者
正则里,竖线 | 表示或者。
例如:
cat|dog
表示匹配 cat 或 dog
来看例子:
import retext = "I have a cat, you have a dog, he has a bird"result = re.findall(r"cat|dog", text)print(result)
输出:
['cat', 'dog']
这个写法在多条件匹配时很实用。
比如你想在文本里找“星期一”或者“周一”,就可以写成两种模式并列。
十二、小括号有什么用
小括号 () 有两个常见作用。
第一,分组。 第二,提取。
先说分组。
比如你写:
(ab)+
它表示 ab 这整个部分可以重复一次或多次。
它能匹配:
abababababab
如果没有括号,ab+ 的意思就变了。 它表示 a 后面跟一个或多个 b。
所以括号能改变规则作用的范围。
再说提取。
比如你匹配一个日期:
(\d{4})-(\d{2})-(\d{2})
这其实把年、月、日分成了三组。 后面你可以单独拿出来用。
这个能力在后面的提取实战里特别关键。
十三、为什么很多人觉得正则难
因为它和普通代码不太一样。
平时写 Python,逻辑是一步一步展开的。 可正则是把规则压缩成一小串符号。
所以你第一次看时,会觉得它像密码。
比如这一串:
^\w{6,12}$
刚看可能很懵。
但如果拆开看,其实很简单:
^ 表示开头\w 表示字母、数字、下划线{6,12} 表示出现 6 到 12 次$ 表示结尾
合起来就是:
整个字符串必须由字母、数字、下划线组成,长度是 6 到 12 位。
你看,一旦拆开,正则并没有那么神秘。
很多人觉得难,不是因为它真的难。 而是因为总想一眼把整串看懂。
正确方法不是硬看。 而是拆着看,一段一段理解。
十四、学正则最容易踩的坑
第一个坑,是把正则当普通字符串看。
比如你写:
"\d+"
虽然很多时候也能用,但更推荐写成:
r"\d+"
前面的 r 表示原始字符串。
为什么这么写更好
因为反斜杠在 Python 字符串里本身就有特殊含义。 加了 r 之后,正则表达式会更稳定,也更不容易出奇怪问题。
以后写正则,尽量养成习惯:
r"你的正则规则"
第二个坑,是贪多求快。
很多人一上来就想背特别复杂的规则。 其实没必要。
你先把最常见的几类学会就够了: 字符类 快捷写法 数量词 开头结尾 分组 或者
这几样掌握后,已经能解决大量实际问题。
第三个坑,是只记符号,不理解场景。
正则不是考试题。 真正重要的是,你什么时候该想到用它。
比如看到一堆文本里要批量找手机号、邮箱、日期、编号,这时你脑子里就应该冒出一句: 这种事适合用正则。
这种场景意识,比死记语法更重要。
十五、看一个稍微真实一点的小例子
假设现在有这样一段文本:
text = "用户A电话13812345678,用户B电话15600001111,用户C电话不是手机号"
现在你的目标是,把里面所有 11 位手机号找出来。
代码可以这样写:
import retext = "用户A电话13812345678,用户B电话15600001111,用户C电话不是手机号"phones = re.findall(r"\d{11}", text)print(phones)
输出:
['13812345678', '15600001111']
这段代码虽然短,但已经体现了正则的价值。
你不需要知道具体号码是什么。 只需要告诉程序,帮我找所有连续 11 位数字。
程序就会自动把符合规则的内容提取出来。
这就是正则的核心魅力。
十六、再看一个文件名匹配的例子
比如有一串文件名:
text = "a.jpg b.png c.jpg d.gif e.jpg"
你想找出所有 jpg 文件。
写法可以是:
import retext = "a.jpg b.png c.jpg d.gif e.jpg"result = re.findall(r"\w+\.jpg", text)print(result)
输出:
['a.jpg', 'c.jpg', 'e.jpg']
这里有个地方要注意。
点号 . 在正则里默认表示任意字符。 如果你真的想表示英文句号本身,就要写成:
\.
这个反斜杠,就是转义。
也就是说:
普通的 . 是规则符号 转义后的 \. 才表示真正的点号
这个点以后你会经常遇到。
十七、你现在不需要追求一口吃成胖子
正则最容易把人劝退的地方,就是它的符号太集中。
但你完全没必要一开始就去研究特别复杂的写法。
对初学者来说,第一步只要做到下面这些,就已经很好了。
看到文本提取问题,知道可以考虑正则。 能看懂最基础的字符匹配规则。 会用数量词控制次数。 知道 ^ 和 $ 是开头结尾。 知道 () 可以分组。 知道 [] 表示一类字符。
掌握这些之后,后面的手机号、邮箱、日期提取,就已经能上手了。
所以学这一章,不要求你马上成为正则高手。 重点是先建立感觉。
你要开始习惯这样看问题: 这段文本里,有没有什么“有规律”的内容可以用模式匹配
只要有这个意识,后面提升会很快。
十八、正则到底适合解决什么问题
你可以记住一句很实用的话:
只要是文本里“有规律但不固定”的内容,正则通常都能帮上忙。
比如:
电话号码,每个号码不同,但位数有规律。 邮箱地址,内容不同,但结构有规律。 日期时间,值不同,但格式有规律。 订单号、快递单号、验证码、日志编号,也都是类似。
所以正则不是用来替代所有字符串操作的。 而是专门用来处理这类规则型文本。
当内容固定时,普通字符串方法更直接。 当内容不固定,但规律明确时,正则就会非常好用。
本章小结
正则表达式,本质上是一种文本匹配规则。 它最适合处理的,不是固定文本,而是那些有规律但不固定的内容。
这一章你要记住几个最基础的概念。
点号 . 表示任意单个字符。 中括号 [] 表示一类字符。\d 表示数字,\w 表示字母数字下划线,\s 表示空白字符。*、+、?、{n}、{m,n} 用来控制出现次数。^ 和 $ 用来限制开头和结尾。() 可以分组,| 表示或者。
学正则最重要的,不是背符号。 而是学会把问题转成规则。
当你能从“我要找某个具体内容”,变成“我要找一类符合规律的内容”时,正则才算真正入门。
课后练习
你可以自己试着做这几个小练习。
第一,写一段文本,把里面所有数字提取出来。
第二,写一段带有多个文件名的字符串,提取出所有 .txt 文件名。
第三,写一个规则,判断一个字符串是不是 6 到 12 位的字母、数字或下划线。
第四,准备一段包含日期的文本,比如 2026-03-29、2025-12-01,尝试把所有日期匹配出来。
这些练完之后,下一章讲 re 模块实战时,你会轻松很多。