小白学 Python
AI 编程指南
© 2026 小白学 Python · 基于 walter201230/Python 教程
课程目录关于本站联系方式隐私政策GitHub

全部课程

  1. 00为什么学 Python
  2. 01Python 安装与第一个程序
  3. 02基本数据类型与变量
  4. 03List 与 Tuple
  5. 04Dict 与 Set
  6. 05条件与循环
  7. 06函数
  8. 07迭代
  9. 08面向对象
  10. 09模块
  11. 10魔法方法
  12. 11枚举类
  13. 12元类 metaclass
  14. 13线程与进程
  15. 14正则表达式
  16. 15闭包
  17. 16装饰器
  18. 17代码可读性
  19. 18pathlib:现代化路径处理
  20. 19异常处理
  21. 20dataclass
  22. 21上下文管理器(with)
  23. 22async / await
  24. 23pyproject.toml + uv
  25. 24ruff:一站式代码检查
  26. 25pytest:测试框架
  27. 26日志 logging
  28. 27打包发布到 PyPI
  29. 28学完之后

正则表达式

完整讲解 · 8 段教学·配 4 道练习题·预计 45 分钟

本页是本章的通读版,可直接读完全部讲解。想动手写代码、跑判分,去 闯关模式。

教学 01 / 08

目录

文本
一步一步了解正则表达式
        │
        ├── 初识 Python 正则表达式
        ├── 字符集
        ├── 数量词
        ├── 边界匹配符和组
        ├── re.sub
        └── re.match 和 re.search
教学 02 / 08

初识 Python 正则表达式

正则表达式是一个特殊的字符序列,用于判断一个字符串是否与我们所设定的字符序列是否匹配,也就是说检查一个字符串是否与某种模式匹配。

Python 自 1.5 版本起增加了re 模块,它提供 Perl 风格的正则表达式模式。re 模块使 Python 语言拥有全部的正则表达式功能。

下面通过实例,一步一步来初步认识正则表达式。

比如在一段字符串中寻找是否含有某个字符或某些字符,通常我们使用内置函数来实现,如下:

Python· 可在网页运行
在闯关页练习 →
# 设定一个常量
a = '两点水|twowater|liangdianshui|草根程序员|ReadingWithU'

# 判断是否有 “两点水” 这个字符串,使用 PY 自带函数

print(f'是否含有“两点水”这个字符串:{a.index("两点水") > -1}')
print(f'是否含有“两点水”这个字符串:{"两点水" in a}')

输出的结果如下:

文本
是否含有“两点水”这个字符串:True
是否含有“两点水”这个字符串:True

那么,如果使用正则表达式呢?

刚刚提到过,Python 给我们提供了 re 模块来实现正则表达式的所有功能,那么我们先使用其中的一个函数:

Python· 语法示意
re.findall(pattern, string[, flags])

该函数实现了在字符串中找到正则表达式所匹配的所有子串,并组成一个列表返回,具体操作如下:

Python· 可在网页运行
在闯关页练习 →

import re

# 设定一个常量
a = '两点水|twowater|liangdianshui|草根程序员|ReadingWithU'

# 正则表达式

findall = re.findall('两点水', a)
print(findall)

if len(findall) > 0:
    print('a 含有“两点水”这个字符串')
else:
    print('a 不含有“两点水”这个字符串')

输出的结果:

文本
['两点水']
a 含有“两点水”这个字符串

从输出结果可以看到,可以实现和内置函数一样的功能,可是在这里也要强调一点,上面这个例子只是方便我们理解正则表达式,这个正则表达式的写法是毫无意义的。为什么这样说呢?

因为用 Python 自带函数就能解决的问题,我们就没必要使用正则表达式了,这样做多此一举。而且上面例子中的正则表达式设置成为了一个常量,并不是一个正则表达式的规则,正则表达式的灵魂在于规则,所以这样做意义不大。

那么正则表达式的规则怎么写呢?先不急,我们一步一步来,先来一个简单的,找出字符串中的所有小写字母。首先我们在 findall 函数中第一个参数写正则表达式的规则,其中 [a-z] 就是匹配任何小写字母,第二个参数只要填写要匹配的字符串就行了。具体如下:

Python· 可在网页运行
在闯关页练习 →

import re

# 设定一个常量
a = '两点水|twowater|liangdianshui|草根程序员|ReadingWithU'

# 选择 a 里面的所有小写英文字母

re_findall = re.findall('[a-z]', a)

print(re_findall)

输出的结果:

文本
['t', 'w', 'o', 'w', 'a', 't', 'e', 'r', 'l', 'i', 'a', 'n', 'g', 'd', 'i', 'a', 'n', 's', 'h', 'u', 'i', 'e', 'a', 'd', 'i', 'n', 'g', 'i', 't', 'h']

这样我们就拿到了字符串中的所有小写字母了。

给规则同时准备能匹配和不能匹配的例子

前面的规则已经一步步写出来了。现在把要求缩小一点:一段名称必须由一个或多个英文字母组成。各位先为它准备三种输入,再看结果:

Python· 可在网页运行
在闯关页练习 →
import re

pattern = r'[A-Za-z]+'
for name in ['Python', 'Python20', '']:
    print(bool(re.fullmatch(pattern, name)))

输出:

文本
True
False
False

fullmatch 要求整段文字符合规则。如果改用 search,'Python20' 里面的 'Python' 就可以被找到,但这不等于整个名称都符合要求。先弄清楚是「包含某一段」还是「整段满足」,再选择函数。

自己再加上 'python'、'Python!' 和 '两点水',这里应依次得到 True、False、False。最后一个结果来自我们限定英文字母的需求,不是中文不能用正则处理。

使用 index() 寻找子串时,找不到内容会抛出 ValueError,不会返回 -1 供比较。如果只想判断有没有某段普通文字,'目标' in text 更直接。规则越复杂,越需要保留几组正例、反例和边界输入。

教学 03 / 08

字符集

好了,通过上面的几个实例我们初步认识了 Python 的正则表达式,可能你就会问,正则表达式还有什么规则,什么字母代表什么意思呢?

其实,这些都不急,在本章后面会给出对应的正则表达式规则列表,而且这些东西在网上随便都能 Google 到。所以现在,我们还是进一步加深对正则表达式的理解,讲一下正则表达式的字符集。

字符集是由一对方括号 “[]” 括起来的字符集合。使用字符集,可以匹配多个字符中的一个。

举个例子,比如你使用 C[ET]O 匹配到的是 CEO 或 CTO ,也就是说 [ET] 代表的是一个 E 或者一个 T 。像上面提到的 [a-z] ,就是所有小写字母中的其中一个,这里使用了连字符 “-” 定义一个连续字符的字符范围。当然,像这种写法,里面可以包含多个字符范围的,比如:[0-9a-fA-F] ,匹配单个的十六进制数字,且不分大小写。注意了,字符和范围定义的先后顺序对匹配的结果是没有任何影响的。

其实说了那么多,只是想证明,字符集一对方括号 “[]” 里面的字符关系是"或(OR)"关系,下面看一个例子:

Python· 可在网页运行
在闯关页练习 →

import re
a = 'uav,ubv,ucv,uwv,uzv,ucv,uov'

# 字符集

# 取 u 和 v 中间是 a 或 b 或 c 的字符
findall = re.findall('u[abc]v', a)
print(findall)
# 如果是连续的字母,数字可以使用 - 来代替
l = re.findall('u[a-c]v', a)
print(l)

# 取 u 和 v 中间不是 a 或 b 或 c 的字符
re_findall = re.findall('u[^abc]v', a)
print(re_findall)

输出的结果:

文本
['uav', 'ubv', 'ucv', 'ucv']
['uav', 'ubv', 'ucv', 'ucv']
['uwv', 'uzv', 'uov']

在例子中,使用了取反字符集,也就是在左方括号 “[” 后面紧跟一个尖括号 “^”,就会对字符集取反。需要记住的一点是,取反字符集必须要匹配一个字符。比如:q[^u] 并不意味着:匹配一个 q,后面没有 u 跟着。它意味着:匹配一个 q,后面跟着一个不是 u 的字符。具体可以对比上面例子中输出的结果来理解。

我们都知道,正则表达式本身就定义了一些规则,比如 \d,匹配所有数字字符,其实它是等价于 [0-9],下面也写了个例子,通过字符集的形式解释了这些特殊字符。

对 Python 的字符串正则,默认采用 Unicode 规则,\d 还可以匹配全角数字等字符,\w 也不限于英文字母、数字与下划线。只有限定 ASCII 规则时,才能按下面的 ASCII 字符范围理解。写正则模式时优先使用 r"\d" 这样的原始字符串,避免反斜杠被字符串语法处理。

Python· 可在网页运行
在闯关页练习 →
import re

a = 'uav_ubv_ucv_uwv_uzv_ucv_uov&123-456-789'

# 概括字符集

# \d 相当于 [0-9] ,匹配所有数字字符
# \D 相当于 [^0-9] , 匹配所有非数字字符
findall1 = re.findall('\d', a)
findall2 = re.findall('[0-9]', a)
findall3 = re.findall('\D', a)
findall4 = re.findall('[^0-9]', a)
print(findall1)
print(findall2)
print(findall3)
print(findall4)

# \w 匹配包括下划线的任何单词字符,等价于 [A-Za-z0-9_]
findall5 = re.findall('\w', a)
findall6 = re.findall('[A-Za-z0-9_]', a)
print(findall5)
print(findall6)

输出结果:

文本
['1', '2', '3', '4', '5', '6', '7', '8', '9']
['1', '2', '3', '4', '5', '6', '7', '8', '9']
['u', 'a', 'v', '_', 'u', 'b', 'v', '_', 'u', 'c', 'v', '_', 'u', 'w', 'v', '_', 'u', 'z', 'v', '_', 'u', 'c', 'v', '_', 'u', 'o', 'v', '&', '-', '-']
['u', 'a', 'v', '_', 'u', 'b', 'v', '_', 'u', 'c', 'v', '_', 'u', 'w', 'v', '_', 'u', 'z', 'v', '_', 'u', 'c', 'v', '_', 'u', 'o', 'v', '&', '-', '-']
['u', 'a', 'v', '_', 'u', 'b', 'v', '_', 'u', 'c', 'v', '_', 'u', 'w', 'v', '_', 'u', 'z', 'v', '_', 'u', 'c', 'v', '_', 'u', 'o', 'v', '1', '2', '3', '4', '5', '6', '7', '8', '9']
['u', 'a', 'v', '_', 'u', 'b', 'v', '_', 'u', 'c', 'v', '_', 'u', 'w', 'v', '_', 'u', 'z', 'v', '_', 'u', 'c', 'v', '_', 'u', 'o', 'v', '1', '2', '3', '4', '5', '6', '7', '8', '9']
教学 04 / 08

数量词

来,继续加深对正则表达式的理解,这部分理解一下数量词,为什么要用数量词,想想都知道,如果你要匹配几十上百的字符时,难道你要一个一个的写,所以就出现了数量词。

数量词的词法是:{min,max} 。min 和 max 都是非负整数。如果逗号有而 max 被忽略了,则 max 没有限制。如果逗号和 max 都被忽略了,则重复 min 次。比如,\b[1-9][0-9]{3}\b,匹配的是 1000 ~ 9999 之间的数字( “\b” 表示单词边界),而 \b[1-9][0-9]{2,4}\b,匹配的是一个在 100 ~ 99999 之间的数字。

下面看一个实例,匹配出字符串中 4 到 7 个字母的英文

Python· 可在网页运行
在闯关页练习 →
import re

a = 'java*&39android##@@python'

# 数量词

findall = re.findall('[a-z]{4,7}', a)
print(findall)

输出结果:

文本
['java', 'android', 'python']

注意,这里有贪婪和非贪婪之分。那么我们先看下相关的概念:

贪婪模式:它的特性是一次性地读入整个字符串,如果不匹配就吐掉最右边的一个字符再匹配,直到找到匹配的字符串或字符串的长度为 0 为止。它的宗旨是读尽可能多的字符,所以当读到第一个匹配时就立刻返回。

懒惰模式:它的特性是从字符串的左边开始,试图不读入字符串中的字符进行匹配,失败,则多读一个字符,再匹配,如此循环,当找到一个匹配时会返回该匹配的字符串,然后再次进行匹配直到字符串结束。

上面例子中的就是贪婪的,如果要使用非贪婪,也就是懒惰模式,怎么呢?

如果要使用非贪婪,则加一个 ? ,上面的例子修改如下:

Python· 可在网页运行
在闯关页练习 →
import re

a = 'java*&39android##@@python'

# 贪婪与非贪婪

re_findall = re.findall('[a-z]{4,7}?', a)
print(re_findall)

输出结果如下:

文本
['java', 'andr', 'pyth']

从输出的结果可以看出,android 只打印除了 andr ,Python 只打印除了 pyth ,因为这里使用的是懒惰模式。

当然,还有一些特殊字符也是可以表示数量的,比如:

?:告诉引擎匹配前导字符 0 次或 1 次

+:告诉引擎匹配前导字符 1 次或多次

*:告诉引擎匹配前导字符 0 次或多次

把这部分的知识点总结一下,就是下面这个表了:

贪 婪惰 性描 述
???零次或一次出现,等价于{0,1}
++?一次或多次出现 ,等价于{1,}
**?零次或多次出现 ,等价于{0,}
{n}{n}?恰好 n 次出现
{n,m}{n,m}?至少 n 次枝多 m 次出现
{n,}{n,}?至少 n 次出现
教学 05 / 08

边界匹配符和组

将上面几个点,就用了很大的篇幅了,现在介绍一些边界匹配符和组的概念。

一般的边界匹配符有以下几个:

语法描述
^匹配字符串开头(在有多行的情况中匹配每行的开头)
$匹配字符串的末尾(在有多行的情况中匹配每行的末尾)
\A仅匹配字符串开头
\Z仅匹配字符串末尾
\b匹配 \w 和 \W 之间
\B[^\b]

分组,被括号括起来的表达式就是分组。分组表达式 (...) 其实就是把这部分字符作为一个整体,当然,可以有多分组的情况,每遇到一个分组,编号就会加 1 ,而且分组后面也是可以加数量词的。

此处本应有例子,考虑到篇幅问题,就不贴了

教学 06 / 08

re.sub

实战过程中,我们很多时候需要替换字符串中的字符,这时候就可以用到 def sub(pattern, repl, string, count=0, flags=0) 函数了,re.sub 共有五个参数。其中三个必选参数:pattern, repl, string ; 两个可选参数:count, flags .

具体参数意义如下:

参数描述
pattern表示正则中的模式字符串
replrepl,就是replacement,被替换的字符串的意思
string即表示要被处理,要被替换的那个 string 字符串
count对于pattern中匹配到的结果,count可以控制对前几个group进行替换
flags正则表达式修饰符

具体使用可以看下下面的这个实例,注释都写的很清楚的了,主要是注意一下,第二个参数是可以传递一个函数的,这也是这个方法的强大之处,例如例子里面的函数 convert ,对传递进来要替换的字符进行判断,替换成不同的字符。

Python· 可在网页运行
在闯关页练习 →
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

import re

a = 'Python*Android*Java-888'

# 把字符串中的 * 字符替换成 & 字符
sub1 = re.sub('\*', '&', a)
print(sub1)

# 把字符串中的第一个 * 字符替换成 & 字符
sub2 = re.sub('\*', '&', a, 1)
print(sub2)


# 把字符串中的 * 字符替换成 & 字符,把字符 - 换成 |

# 1、先定义一个函数
def convert(value):
    group = value.group()
    if (group == '*'):
        return '&'
    elif (group == '-'):
        return '|'


# 第二个参数,要替换的字符可以为一个函数
sub3 = re.sub('[\*-]', convert, a)
print(sub3)

输出的结果:

文本
Python&Android&Java-888
Python&Android*Java-888
Python&Android&Java|888
教学 07 / 08

re.match 和 re.search

re.match 函数

语法:

Python· 结合前文阅读
re.match(pattern, string, flags=0)

re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match() 就返回 none。

re.search 函数

语法:

Python· 结合前文阅读
re.search(pattern, string, flags=0)

re.search 扫描整个字符串并返回第一个成功的匹配。

re.match 和 re.search 的参数,基本一致的,具体描述如下:

参数描述
pattern匹配的正则表达式
string要匹配的字符串
flags标志位,用于控制正则表达式的匹配方式,如:是否区分大小写

那么它们之间有什么区别呢?

re.match 只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回 None;而 re.search 匹配整个字符串,直到找到一个匹配。这就是它们之间的区别了。

re.match 和 re.search 在网上有很多详细的介绍了,可是再个人的使用中,还是喜欢使用 re.findall

看下下面的实例,可以对比下 re.search 和 re.findall 的区别,还有多分组的使用。具体看下注释,对比一下输出的结果:

示例:

Python· 可在网页运行
在闯关页练习 →

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

# 提取图片的地址

import re

a = '<img src="https://s-media-cache-ak0.pinimg.com/originals/a8/c4/9e/a8c49ef606e0e1f3ee39a7b219b5c05e.jpg">'

# 使用 re.search
search = re.search('<img src="(.*)">', a)
# group(0) 是一个完整的分组
print(search.group(0))
print(search.group(1))

# 使用 re.findall
findall = re.findall('<img src="(.*)">', a)
print(findall)

# 多个分组的使用(比如我们需要提取 img 字段和图片地址字段)
re_search = re.search('<(.*) src="(.*)">', a)
# 打印 img
print(re_search.group(1))
# 打印图片地址
print(re_search.group(2))
# 打印 img 和图片地址,以元祖的形式
print(re_search.group(1, 2))
# 或者使用 groups
print(re_search.groups())

输出的结果:

文本
<img src="https://s-media-cache-ak0.pinimg.com/originals/a8/c4/9e/a8c49ef606e0e1f3ee39a7b219b5c05e.jpg">
https://s-media-cache-ak0.pinimg.com/originals/a8/c4/9e/a8c49ef606e0e1f3ee39a7b219b5c05e.jpg
['https://s-media-cache-ak0.pinimg.com/originals/a8/c4/9e/a8c49ef606e0e1f3ee39a7b219b5c05e.jpg']
img
https://s-media-cache-ak0.pinimg.com/originals/a8/c4/9e/a8c49ef606e0e1f3ee39a7b219b5c05e.jpg
('img', 'https://s-media-cache-ak0.pinimg.com/originals/a8/c4/9e/a8c49ef606e0e1f3ee39a7b219b5c05e.jpg')
('img', 'https://s-media-cache-ak0.pinimg.com/originals/a8/c4/9e/a8c49ef606e0e1f3ee39a7b219b5c05e.jpg')

最后,正则表达式是非常厉害的工具,通常可以用来解决字符串内置函数无法解决的问题,而且正则表达式大部分语言都是有的。Python 的用途很多,但在爬虫和数据分析这两个模块中都是离不开正则表达式的。所以正则表达式对于学习 Python 来说,真的很重要。

练习 1 / 4用 re.findall 抓出全部数字

字符串 'abc123def456ghi789' 里夹杂着一些连续的数字段。

请用 re.findall 把所有连续数字段抓出来,赋给变量 nums,然后 print(nums)。

输出应该是:

文本
['123', '456', '789']

提示:连续数字可以用 \d+。

💡 提示(3 条)
  1. 记得 import re

  2. 正则用 r'...' 原始字符串避免反斜杠转义

  3. 完整代码:

    Python
    import re
    s = 'abc123def456ghi789'
    nums = re.findall(r'\d+', s)
    print(nums)
在编辑器里写这题,跑判分、看参考答案去闯关页做这题 →
练习 2 / 4用 re.match 校验手机号

请写一个函数 is_phone(s):判断字符串 s 是不是合法的中国大陆手机号。

规则:

  • 总共 11 位
  • 第 1 位必须是 1
  • 第 2 位是 3 ~ 9
  • 后面 9 位都是数字

用 re.match 实现。匹配成功返回 True,否则 False。

然后调用:

Python
print(is_phone('13812345678'))
print(is_phone('12345'))
print(is_phone('1381234567a'))

输出应该是:

文本
True
False
False

提示:用 ^1[3-9]\d{9}$ 这条正则。拆开看:^ 锁定字符串开头、$ 锁定结尾(少了它们,半截字符串也能匹配上);[3-9] 表示 3 到 9 之间任意一个数字;\d{9} 表示连续 9 个数字。

💡 提示(3 条)
  1. re.match 返回 Match 对象或 None,bool(...) 转成 True/False

  2. 正则里 ^ 和 $ 锁定开头和结尾,避免半截字符串也通过

  3. 完整代码:

    Python
    import re
    def is_phone(s):
        return bool(re.match(r'^1[3-9]\d{9}$', s))
    print(is_phone('13812345678'))
    print(is_phone('12345'))
    print(is_phone('1381234567a'))
在编辑器里写这题,跑判分、看参考答案去闯关页做这题 →
练习 3 / 4用分组提取邮箱的用户名和域名

已知字符串 email = 'alice@example.com'。

请用 re.match + 分组 (...),把用户名和域名分别抽出来,依次 print。

要求:

  • 第 1 个分组匹配用户名(@ 之前的部分)
  • 第 2 个分组匹配域名(@ 之后的部分)
  • 用 m.group(1) 取用户名,m.group(2) 取域名

输出应该是:

文本
alice
example.com

提示:可以用正则 (\w+)@(\w+\.\w+)。

💡 提示(3 条)
  1. 分组就是把想抽出来的部分用 () 包起来

  2. \. 匹配字面意义的点;不加反斜杠 . 是任意字符

  3. 完整代码:

    Python
    import re
    email = 'alice@example.com'
    m = re.match(r'(\w+)@(\w+\.\w+)', email)
    print(m.group(1))
    print(m.group(2))
在编辑器里写这题,跑判分、看参考答案去闯关页做这题 →
练习 4 / 4用 re.sub 把多空格压成单空格

已知一段被「多空格」污染的文本:

Python
s = 'hello   world  python  is   cool'

请用 re.sub 把一段或多段连续的空白替换成一个空格,赋给变量 cleaned,然后 print(cleaned)。

输出应该是:

文本
hello world python is cool

提示:\s+ 匹配「一个或多个空白字符」。

💡 提示(3 条)
  1. re.sub(pattern, repl, string):第二个参数是替换值

  2. 用 \s+ 匹配「一个或多个空白」,加号是 1 次或多次

  3. 完整代码:

    Python
    import re
    s = 'hello   world  python  is   cool'
    cleaned = re.sub(r'\s+', ' ', s)
    print(cleaned)
在编辑器里写这题,跑判分、看参考答案去闯关页做这题 →
教学 08 / 08

Python 学习指南

常用的正则表达式

校验数字的表达式

文本
1、  数字:^[0-9]*$
2、  n位的数字:^\d{n}$
3、  至少n位的数字:^\d{n,}$
4、  m-n位的数字:^\d{m,n}$
5、  零和非零开头的数字:^(0|[1-9][0-9]*)$
6、  非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$
7、  带1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})?$
8、  正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$
9、  有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
10、 有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
11、 非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$
12、 非零的负整数:^\-[1-9][]0-9"*$ 或 ^-[1-9]\d*$
13、 非负整数:^\d+$ 或 ^[1-9]\d*|0$
14、 非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$
15、 非负浮点数:^\d+(\.\d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$
16、 非正浮点数:^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$
17、 正浮点数:^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
18、 负浮点数:^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ 或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
19、 浮点数:^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$

校验字符的表达式

文本
1、  汉字:^[\u4e00-\u9fa5]{0,}$
2、  英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
3、  长度为3-20的所有字符:^.{3,20}$
4、  由26个英文字母组成的字符串:^[A-Za-z]+$
5、  由26个大写英文字母组成的字符串:^[A-Z]+$
6、  由26个小写英文字母组成的字符串:^[a-z]+$
7、  由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
8、  由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$
9、  中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$
10、 中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
11、 可以输入含有^%&',;=?$\"等字符:[^%&',;=?$\x22]+
12、 禁止输入含有~的字符:[^~\x22]+

特殊需求表达式

文本
1、  Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
2、  域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
3、  InternetURL:[a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
4、  手机号码:^(13[0-9]|14[0-9]|15[0-9]|166|17[0-9]|18[0-9]|19[8|9])\d{8}$
5、  电话号码("XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX):^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$
6、  国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
7、  18位身份证号码(数字、字母x结尾):^((\d{18})|([0-9x]{18})|([0-9X]{18}))$
8、  帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
9、  密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$
10、 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$  
11、 日期格式:^\d{4}-\d{1,2}-\d{1,2}
12、 一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
13、 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
14、 钱的输入格式:
      a.有四种钱的表示形式我们可以接受:"10000.00" 和 "10,000.00", 和没有 "分" 的 "10000" 和 "10,000":^[1-9][0-9]*$
      b.这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$
      c.一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$
      d.这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$
      e.必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10" 和 "10.2" 是通过的:^[0-9]+(.[0-9]{2})?$
      f.这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$
      g.这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$
      h.1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$
15、 xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
16、 中文字符的正则表达式:[\u4e00-\u9fa5]
17、 双字节字符:[^\x00-\xff]    (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
18、 空白行的正则表达式:\n\s*\r    (可以用来删除空白行)
19、 HTML标记的正则表达式:<(\S*?)[^>]*>.*?</\1>|<.*? />    (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)
20、 首尾空白字符的正则表达式:^\s*|\s*$或(^\s*)|(\s*$)    (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
21、 腾讯QQ号:[1-9][0-9]{4,}    (腾讯QQ号从10000开始)
22、 中国邮政编码:[1-9]\d{5}(?!\d)    (中国邮政编码为6位数字)
23、 IP地址:\d+\.\d+\.\d+\.\d+    (提取IP地址时有用)
24、 IP地址:((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))  
25、PV6 地址: (([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))
26、URL 链接: ((http|ftp|https)://)(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\&%_\./-~-]*)?

读完了?动手练一遍才算真会。

去闯关模式练习 →
← 上一章13 · 线程与进程
正则表达式
下一章 →15 · 闭包
本章目录

正则表达式

  1. 教学 01目录
  2. 教学 02初识 Python 正则表达式
  3. 教学 03字符集
  4. 教学 04数量词
  5. 教学 05边界匹配符和组
  6. 教学 06re.sub
  7. 教学 07re.match 和 re.search
  8. 练习 1用 re.findall 抓出全部数字
  9. 练习 2用 re.match 校验手机号
  10. 练习 3用分组提取邮箱的用户名和域名
  11. 练习 4用 re.sub 把多空格压成单空格
  12. 教学 08Python 学习指南

学习目录