一、正则表达式概述
- 正则表达式,又称规则表达式,英文名为 Regular Expression,在代码中常简写为regex、regexp 或 RE,是计算机科学的一个概念。通常被用来检索、替换符合某个模式(规则)的文本。
- 正则表达式 是对 字符串 和 特殊字符(称为 “元字符” )操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
- 许多程序设计语言都支持利用正则表达式进行字符串操作。在 Python 的标准库中就内建了一个功能强大的正则表达式模块,即 re 模块。
二、正则表达式常用场景
-
测试字符串内的模式。
- 可以测试输入字符串,以查看字符串内是否出现电话号码模式或信用卡号码模式。这称为 数据验证。 替换文本。
- 可以使用正则表达式来识别文档中的特定文本,完全删除该文本或者用其他文本替换它。 从字符串中提取子字符串。
- 可以使用正则表达式来查找文档内或输入域内特定的文本,将获取的数据保存下来。
三、正则表达式的使用
Python 自1.5版本起增加了 re 模块。
re 模块使 Python 语言拥有全部的正则表达式功能。
这里主要介绍Python中的正则表达式。
普通字符
普通字符 包括没有显式指定为 元字符 的所有可打印和不可打印字符。这包括所有大小写字母、所有数字、所有标点符号和一些其他符号。
非打印字符
非打印字符 也可以是正则表达式的组成部分。下表列出了表示非打印字符的转义序列:
字符 | 描述 |
---|---|
\cx | 匹配由 x 指明的 控制字符。例如, \cM 匹配一个 Control-M 或回车符。 |
\f | 匹配一个 换页符。等价于 \x0c 和 \cL。 |
\n | 匹配一个 换行符。等价于 \x0a 和 \cJ。 |
\r | 匹配一个 回车符。等价于 \x0d 和 \cM。 |
\s | 匹配 任何空白字符,包括空格、制表符、换页符等等。等价于 [\f\n\r\t\v]。 |
\S | 匹配 任何非空白字符。等价于 [^\f\n\r\t\v]。 |
\t | 匹配一个 制表符。等价于 \x09 和 \cI。 |
\v | 匹配一个 垂直制表符。等价于 \x0b 和 \cK。 |
元字符
元字符,就是一些有 特殊含义 的字符,如 *,简单的说就是表示任何字符的意思。如果要查找字符串中的 * 符号,则需要对 * 进行转义,即在其前加一个反斜杠字符 \,即 \*。
若要匹配这些特殊字符,必须首先使字符”转义”,即将反斜杠字符 \ 放在它们前面。下表列出了正则表达式中的 元字符:
字符 | 描述 |
---|---|
. | 匹配除换行符 \n 之外的任何 单字符。要匹配 . ,请使用 \. 。 |
* | 匹配前面的 子表达式 零次或多次。要匹配 * 字符,请使用 \*。 |
+ | 匹配前面的 子表达式 一次或多次。要匹配 + 字符,请使用 \+。 |
? | 匹配前面的 子表达式 零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。 |
^ | 匹配输入字符串的开始位置,在方括号表达式中使用时,表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。 |
$ | 匹配字符串的结尾位置。如果设置了Multiline 属性,则 $ 也匹配 \n 或 \r。要匹配 $ 字符本身,请使用 \$。 |
\ | 转义字符。将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。 |
| | 关系符号“或”。指明两项之间的一个选择。要匹配 |,请使用 \|。 |
* 、+ 都是贪婪的,因为它们会尽可能多的匹配文字,在它们的后面加上一个 ? 就可以实现非贪婪或最小匹配。
限定符
限定符 用来指定正则表达式的一个给定 子字符串 必须要出现多少次才能满足匹配。正则表达式的限定符有:
字符 | 描述 |
---|---|
( ) | 标记一个 子表达式 的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。 |
[ ] | 标记一个 中括号表达式 的开始和结束位置。表示字符集合。要匹配这些字符,请使用 \ [ 和 \ ]。 |
{ } | 标记 限定符表达式 的开始和结束位置。要匹配这些字符,请使用 \{ 和 \}。 |
{n} | n 是一个非负整数。它前面的 字符 或 子字符串 匹配确定的 n 次。 |
{n,} | n 是一个非负整数。它前面的 字符 或 子字符串 至少匹配n 次。 |
{,m} | m 为非负整数,它前面的 字符 或 子字符串 最多匹配 m 次。 |
{n,m} | n 和 m 均为非负整数,其中n <= m。它前面的 字符 或 子字符串 最少匹配 n 次且最多匹配 m 次。 |
请注意在逗号和数字之间不能有空格。
预定义字符集
字符 | 描述 |
---|---|
\d | 匹配数字:[0-9] |
\D | 匹配非数字:[^\d] |
\s | 匹配任何空白字符:[<空格>\t\r\n\f\v] |
\S | 匹配非空白字符:[^\s] |
\w | 匹配包括下划线在内的任何字符:[A-Za-z0-9_] |
\W | 匹配非字母字符,即匹配特殊字符 |
\A | 仅匹配字符串开头,同^ |
\Z | 仅匹配字符串结尾,同$ |
\b | 匹配一个单词边界,也就是指单词和空格间的位置。 |
\B | [^\b] |
特殊分组用法:
字符 | 描述 |
---|---|
(?P< name >) | 分组,除了原有的编号外再指定一个额外的别名 |
(?P=name) | 引用别名为的分组匹配到的字符串 |
\< number > | 引用编号为的分组匹配到字符串 |
(?imx) | 正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。 |
(?-imx) | 正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。 |
(?: re) | 类似 (…), 但是不表示一个组 |
(?imx: re) | 在括号中使用i, m, 或 x 可选标志 |
(?-imx: re) | 在括号中不使用i, m, 或 x 可选标志 |
(?#…) | 注释 |
(?= re) | 前向肯定界定符。 |
(?! re) | 前向否定界定符。 |
(?> re) | 匹配的独立模式,省去回溯。 |
(?<= re) | 反向(look behind)肯定预查,与正向肯定预查类似,只是方向相反。 |
(? | 反向否定预查,与正向否定预查类似,只是方向相反。 |
正则表达式修饰符 - 可选标志
正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位 OR( | ) 来指定。如 re.I | re.M 被设置成 I 和 M 标志:
修饰符 | 描述 |
---|---|
re.I | 使匹配对大小写不敏感 |
re.L | 做本地化识别(locale-aware)匹配 |
re.M | 多行匹配,影响 ^ 和 $ |
re.S | 使 . 匹配包括换行在内的所有字符 |
re.U | 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B. |
re.X | 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。 |
选择
- 用圆括号将所有选择项括起来,相邻的选择项之间用 | 分隔。但用圆括号会有一个副作用,使相关的匹配会被缓存,此时可用 ?: 放在第一个选项前来消除这种副作用。
- 其中 ?: 是 非捕获元之一,还有两个非捕获元是 ?= 和 ?!,这两个还有更多的含义,前者为正向预查,在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串,后者为反向预查,在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串。
反向引用
- 对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中,所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 \n 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数。
- 可以使用非捕获元字符 ?:、?= 或 ?! 来重写捕获,忽略对相关匹配的保存。
- 反向引用的最简单的、最有用的应用之一,是提供查找文本中两个相同的相邻单词的匹配项的能力。
四、re模块的常用方法
-
re.compile(pattern):创建模式对象
>>>p = re.compile('^[A-Z]')
>>>m = pat.search('CBA') #等价于 re.search('^[A-Z]','CBA')
>>>print(m)
<_sre.SRE_Match object; span=(0, 1), match='C'>
-
re.search(pattern,string,flags = 0 )
-
在字符串内查找模式匹配,只要找到第一个匹配然后返回,如果字符串没有匹配,则返回None。
注:match和search一旦匹配成功,就是一个match object对象,而match object对象有以下方法:
start() 返回匹配开始的位置
end() 返回匹配结束的位置
span() 返回一个元组包含匹配 (开始,结束) 的位置
group()返回re整体匹配的字符串,
group (n,m) 返回组号为n,m所匹配的字符串,如果组号不存在,则返回indexError异常
groups()groups() 方法返回一个包含正则表达式中所有小组字符串的元组,从 1 到所含的小组号,通常groups()不需要参数,返回一个元组,元组中的元就是正则表达式中定义的组。
>>>r = "123abc456"
>>>print(re.search("([0-9]*)([a-z]*)([0-9]*)",r).group(0)) #123abc456,返回整体
>>>print(re.search("([0-9]*)([a-z]*)([0-9]*)",r).group(1)) #123
>>>print(re.search("([0-9]*)([a-z]*)([0-9]*)",r).group(2)) #abc
>>>print(re.search("([0-9]*)([a-z]*)([0-9]*)",r).group(3)) #456
#group(1) 列出第一个括号匹配部分,group(2) 列出第二个括号匹配部分,group(3) 列出第三个括号匹配部分。
-
re.match(pattern,string,flags = 0 )
-
如果字符串开头的零个或多个字符与正则表达式模式匹配,则返回相应的匹配对象。如果字符串与模式不匹配则返回None。
>>> re.match('a','akdfg')
<_sre.SRE_Match object; span=(0, 1), match='a'>
-
re.findall(pattern, string, flags=0)
- 遍历匹配,可以获取字符串中所有匹配的字符串,返回一个列表。
>>>t = "Tina is a good girl, she is cool, clever, and beautiful..."
>>>r = re.compile(r'\w*oo\w*')
>>>print(r.findall(t))
['good', 'cool']
>>>print(re.findall(r'(\w)*oo(\w)',t)) #()表示子表达式
[('g', 'd'), ('c', 'l')]
-
re.fullmatch(pattern,string,flags = 0 )
- 如果整个字符串与正则表达式模式匹配,则返回相应的匹配对象。如果字符串与模式不匹配则返回None。 re.split(pattern, string[, maxsplit])
-
按照能够匹配的子串将string分割后返回列表。
maxsplit用于指定最大分割次数,不指定将全部分割。
>>>print(re.split('\d+','one1two2three3four4five5'))
['one', 'two', 'three', 'four', 'five', '']
-
re.finditer(pattern, string, flags=0)
- 搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器。找到 RE 匹配的所有子串,并把它们作为一个迭代器返回。
>>>iter = re.finditer(r'\d+','12 drumm44ers drumming, 11 ... 10 ...')
>>>for i in iter:
... print(i)
... print(i.group())
...
<_sre.SRE_Match object; span=(0, 2), match='12'>
12
<_sre.SRE_Match object; span=(8, 10), match='44'>
44
<_sre.SRE_Match object; span=(24, 26), match='11'>
11
<_sre.SRE_Match object; span=(31, 33), match='10'>
10
-
re.sub(pattern,repl,string,count = 0,flags = 0 )
-
使用 repl 替换string中每一个匹配的子串后返回替换后的字符串。
第四个参数指替换个数。默认为0,表示每个匹配项都替换。
>>>text = "Good is a handsome boy, he is cool, clever, and so on..."
>>>print(re.sub(r'\s+', '-', text))
Good-is-a-handsome-boy,-he-is-cool,-clever,-and-so-on...
#re.sub还允许使用函数对匹配项的替换进行复杂的处理。
>>>text = "Good is a handsome boy, he is cool, clever, and so on..."
>>>print(re.sub(r'\s+', lambda m:'['+m.group(0)+']', text,0))
JGood[ ]is[ ]a[ ]handsome[ ]boy,[ ]he[ ]is[ ]cool,[ ]clever,[ ]and[ ]so[ ]on...
五、常用的正则表达式
一、校验数字的表达式
- 数字:^[0-9]*$
- n位的数字:^\d{n}$
- 至少n位的数字:^\d{n,}$
- m-n位的数字:^\d{m,n}$
- 零和非零开头的数字:^(0|[1-9][0-9]*)$
- 非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(\.[0-9]{1,2})?$
- 带1- 2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})$
- 正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$
- 有两位小数的正实数:^[0-9]+(\.[0-9]{2})?$
- 有1~3位小数的正实数:^[0-9]+(\.[0-9]{1,3})?$
- 非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$
- 非零的负整数:^\-[1-9][0-9]$ 或 ^-[1-9]\d$
- 非负整数:^\d+$ 或 ^[1-9]\d*|0$
- 非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$
- 非负浮点数:^\d+(\.\d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$
- 非正浮点数:^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$
- 正浮点数:^[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]*))$
- 负浮点数:^-([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]*)))$
- 浮点数:^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$
二、校验字符的表达式
- 汉字:^[\u4e00-\u9fa5]{0,}$
- 英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
- 长度为3-20的所有字符:^.{3,20}$
- 由26个英文字母组成的字符串:^[A-Za-z]+$
- 由26个大写英文字母组成的字符串:^[A-Z]+$
- 由26个小写英文字母组成的字符串:^[a-z]+$
- 由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
- 由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$
- 中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$
- 中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
- 可以输入含有 ^ % & ’ , ; = ? $ \ ” 等字符:[^%&’,;=?$\x22]+
- 禁止输入含有 ~ 的字符:[^~\x22]+
三、特殊需求表达式
- Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
- 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
- InternetURL:[a-zA-z]+://[^\s]* 或 ^http:// ([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
- 手机号码:^1\d{10}$
- 电话号码:^((\d{3,4}-)|\d{3,4}-)?\d{7,8}$
- 国内电话号码:\d{3}-\d{8}|\d{4}-\d{7}
- 电话号码正则表达式(支持手机号码,3-4位区号,7-8位直播号码,1-4位分机号): ((\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$)
- 身份证号,最后一位是校验位,可能为数字或字符X:(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)
- 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
- 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$
- 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$
- 日期格式:^\d{4}-\d{1,2}-\d{1,2}
- 12个月:^(1[0-2])$
- 31天:^(((1|2)[0-9])|30|31)$
- xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\.[x|X][m|M][l|L]$
- 双字节字符:[^\x00-\xff]
- 空白行的正则表达式:\n\s*\r (可以用来删除空白行)
- 腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)
- 中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
- IP地址:((?:(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d))