Python正则表达式入门
文章目录
- Python正则表达式入门
- 1、在Python 中使用正则表达式
- 2、最基础正则表达式
- 3、正则匹配函数
- 正则表达式的字符组匹配
- 获得多个匹配信息
- 2、字符组
- 正则表达式的区间
- 1.区间
- 2.区间取反
- 快捷方式
- 4.快捷方式取反
- 快捷方式
- 4.快捷方式取反
- 1、字符串的开始和结束
- 通配符
- 可选字符
- 重复
- 重复区间
- 开闭区间
- 速写
- 重复符号 `+`
- 重复符号 `*`
1、在Python 中使用正则表达式
正可谓人生苦短,我用Python
。Python
有个特点就是库非常多,自然拥有正则匹配这种常见的库,并且此库已经嵌入在Python
标准库中,使用起来非常方便,只需要在代码中导入re
模块即可。
import re
Python
的re
模块,使得Python
具备了使用全部正则表达式的功能。为了让我们灵活的使用正则表达式,现在咱们的任务就是学习正则在re
库中的使用。
2、最基础正则表达式
正则表达式是一个以简单直观的方式通过寻找模式匹配文本的工具。 听起来比较复杂,实际非常简单,下面开始体验最简单的正则表达式。最简单的正则表达式是些仅包含简单字母数字字符的表达式——不包含任何其他字符,在这种情况下正则表达式完完全全就是一个正常的字符串。
举例说明,我们要匹配张明
,那么张明
这两个字符就是我们需要的正则表达式。
3、正则匹配函数
知道了最基础正则表达式,可是如何在python
中使用了?首先我们学习第一个函数,search()
函数,它的目的是接收一个正则表达式和一个字符串,并返回发现的第一个匹配的字符串。
import re
a = re.search(r'fox','the quick brown fox jumpred') #第一个参数为正则表达式,第二个参数为要处理的字符串
print(a.span()) # span方法获取的是正则表达式匹配到的位置
b = re.search(r'www','the quick brown fox jumpred')
print(b) #如果匹配不到则会返回None
输出如下:
(16, 19)None
如何匹配到了,我们输出他在正则表达式中的位置,如果没有匹配到,则输出为空。
正则表达式的字符组匹配
获得多个匹配信息
在很多常见的场景中需要进行多个匹配,比如在学生名单中过滤出所有的张姓学生的个数。
如果有这种需求咱们可以使用re
模块中的findall
或者 finditer
方法。两个方法的区别在于findall
返回的是一个列表,finditer
返回的是一个生成器。
l = re.findall(r'张','张三 张三丰 张无忌 张小凡')
print(l)
['张', '张', '张', '张']
在这个例子中,我们会发现findall
返回了4
个“张”,这是因为“张”字在后面的字符串中出现了4
次。即findall
返回了所有的匹配信息。
2、字符组
字符组允许匹配一组可能出现的字符,在正则表达式中用[]
表示字符组标志,举个例子。
'I like Python3 and I like python2.7 '
在这句话中,既有大写的Python
,又有全部是小写的python
。如果我要求都匹配出来,这时候该怎么操作了?这就是正则匹配中字符组的威力了。下面看下示例。
a = re.findall(r'[Pp]ython','I like Python3 and I like python2.7 ')
print(a)
可以发现[Pp]
既可以匹配大写的P
也可以匹配小写的p
,这里值的我们注意的是[Pp]
仅匹配一个字符,他表示匹配在这个[]
内的某一个
正则表达式的区间
1.区间
有一些常见的字符组非常大,比如,我们要匹配的是任意数字,如果依照上述代码,每次我们都需要使用[0123456789]
这种方式明显很不明智,而如果要匹配从a-z
的字母,我们也这样编写代码的话,肯定会让我们崩溃。
为了适应这一点,正则表达式引擎在字符组中使用连字符(-)代表区间,所以我们匹配任意数字可以使用[0-9]
,所以如果我们想要匹配所有小写字母,可以写成[a-z]
,想要匹配所有大写字母可以写成[A-Z]
。
可能我们还有个需求:匹配连字符。因为-
会被正则表达式引擎理解为代表连接区间,所以这个时候我们需要对-
进行转义。
示例:
a = re.findall(r'[0-9]','xxx007abc')b = re.findall(r'[a-z]','abc001ABC')c = re.findall(r'[A-Za-z0-9]','abc007ABC')d = re.findall(r'[0-9\-]','0edu 007-edu')print(a)print(b)print(c)print(d)
执行结果如下:
['0', '0', '7']['a', 'b', 'c']['a', 'b', 'c', '0', '0', '7', 'A', 'B', 'C']['0', '0', '0', '7', '-']
2.区间取反
到目前为止,我们定义的字符组都是由可能出现的字符定义,不过有时候我们可能希望根据不会出现的字符定义字符组,例如:匹配不包含数字的字符组。
a = re.findall(r'[^0-9]','xxx007abc')b = re.search(r'[^0-9]','xxx007abc')print(a)print(b)
执行结果如下:
['x', 'x', 'x', 'a', 'b', 'c']<re.Match object; span=(0, 1), match='x'>
可以通过在字符数组开头使用 ^
字符实现取反操作,从而可以反转一个字符组(意味着会匹配任何指定字符之外的所有字符)。
接下来再看一个表达式:n[^e]
这意味着字符n
接下来的字符是除了e
之外所有的字符。
a = re.findall(r'n[^e]','final')b = re.search(r'n[^e]','final')c = re.findall('r[n[^e]]','Python')print(a)print(b)print(c)
执行结果如下:
['na']<re.Match object; span=(2, 4), match='na'>[]
这里我们可以发现a
和b
匹配的是na
,字符a
因为不是e
所以可以被匹配,而变量c
的值为空,在这里正则表达式引擎只匹配到了字符串n
的位置,而n
之后没有任何可以匹配[^e]
的字符了,所以这里也匹配失败。
[]<re.Match object; span=(0, 6), match='master'>
快捷方式
在正则表达式的使用过程中,人们为了快捷表达与方便阅读,提取了几种普通字符组,并在正则表达式引擎中预定义了其快捷方式。如果我们想要定义单词,以目前学到的可能会使用[A-Za-z]
,但是,很多单词都是使用该字母以外的字符。比如中文,以及其他语言。
正则表达式引擎提供了一些快捷方式:
-
\w
,与 “任意单词字符”匹配,在Python3
中,基本上可以匹配任何语言的任意单词。 - 而当我们想要匹配任意数字的时候,也可以使用快捷方式
\d
,d
即digit
。在Python3
中,它除了可以和[0-9]
匹配,还可以和其他语言的数字匹配。 -
\s
快捷方式匹配空白字符,比如空格,tab、换行
等。 -
\b
快捷方式匹配一个长度为0
的字符串,但是,他仅仅在一个单词开始或结尾处匹配,这被称为词边界快捷方式。
快捷方式 | 描述 |
\w | 与任意单词匹配 |
\d | 与任意数字匹配 |
\s | 匹配空白字符,比如空格,换行等 |
\b | 匹配一个长度为0的子串 |
示例1
a = re.findall(r'\w','学好Python 大展拳脚')b = re.search(r'\w','python3')c = re.search(r'\d','编号89757')print(a)print(b)print(c)
执行结果输出如下:
['学', '好', 'P', 'y', 't', 'h', 'o', 'n', '大', '展', '拳', '脚']<re.Match object; span=(0, 1), match='p'><re.Match object; span=(2, 3), match='8'>
这里findall
会返回所有能匹配的值,search
只会返回第一个匹配到的值。
示例2
a = re.findall(r'\bmaster\b','masterxiao-master-xxx master abc') #单词字符后面或前面不与另一个单词字符直接相邻b = re.search(r'\bmaster\b','master')print(a)print(b)
执行结果输出如下:
['master', 'master']None
示例3
a = re.search(r'\smaster\s','masterxiao master xxx')print(a)
执行结果输出如下:
<re.Match object; span=(10, 18), match=' master '>
4.快捷方式取反
之前提到了取反,快捷方式也可以取反, 例如对于\w
的取反为\W
,可以发现将小写改写成大写即可。
注意:这里
\B
有所不同,\b
匹配的是在单词开始或结束位置长度为0的子字符串,而\B
匹配不在单词开始和结束位置的长度为0
的子字符串。
a = re.findall(r'\Bmaster\B','masterxiao master xxx master abc') #单词字符后面或前面不与另一个单词字符直接相邻b = re.search(r'master\B','masterxiao')print(a)print(b)
执行结果输出如下:
[]<re.Match object; span=(0, 6), match='master'>
快捷方式
在正则表达式的使用过程中,人们为了快捷表达与方便阅读,提取了几种普通字符组,并在正则表达式引擎中预定义了其快捷方式。如果我们想要定义单词,以目前学到的可能会使用[A-Za-z]
,但是,很多单词都是使用该字母以外的字符。比如中文,以及其他语言。
正则表达式引擎提供了一些快捷方式:
-
\w
,与 “任意单词字符”匹配,在Python3
中,基本上可以匹配任何语言的任意单词。 - 而当我们想要匹配任意数字的时候,也可以使用快捷方式
\d
,d
即digit
。在Python3
中,它除了可以和[0-9]
匹配,还可以和其他语言的数字匹配。 -
\s
快捷方式匹配空白字符,比如空格,tab、换行
等。 -
\b
快捷方式匹配一个长度为0
的字符串,但是,他仅仅在一个单词开始或结尾处匹配,这被称为词边界快捷方式。
快捷方式 | 描述 |
\w | 与任意单词匹配 |
\d | 与任意数字匹配 |
\s | 匹配空白字符,比如空格,换行等 |
\b | 匹配一个长度为0的子串 |
示例1
a = re.findall(r'\w','学好Python 大展拳脚')b = re.search(r'\w','python3')c = re.search(r'\d','编号89757')print(a)print(b)print(c)
执行结果输出如下:
['学', '好', 'P', 'y', 't', 'h', 'o', 'n', '大', '展', '拳', '脚']<re.Match object; span=(0, 1), match='p'><re.Match object; span=(2, 3), match='8'>
这里findall
会返回所有能匹配的值,search
只会返回第一个匹配到的值。
示例2
a = re.findall(r'\bmaster\b','masterxiao-master-xxx master abc') #单词字符后面或前面不与另一个单词字符直接相邻b = re.search(r'\bmaster\b','master')print(a)print(b)
执行结果输出如下:
['master', 'master']None
示例3
a = re.search(r'\smaster\s','masterxiao master xxx')print(a)
执行结果输出如下:
<re.Match object; span=(10, 18), match=' master '>
4.快捷方式取反
之前提到了取反,快捷方式也可以取反, 例如对于\w
的取反为\W
,可以发现将小写改写成大写即可。
注意:这里
\B
有所不同,\b
匹配的是在单词开始或结束位置长度为0的子字符串,而\B
匹配不在单词开始和结束位置的长度为0
的子字符串。
a = re.findall(r'\Bmaster\B','masterxiao master xxx master abc') #单词字符后面或前面不与另一个单词字符直接相邻b = re.search(r'master\B','masterxiao')print(a)print(b)
执行结果输出如下:
[]<re.Match object; span=(0, 6), match='master'>
1、字符串的开始和结束
在日常生活中,一个事情的开始与结束都是一件比较重要的事情,在字符串匹配的过程中也是如此,字符串的开始与结束式一个重要的特征。比如我们要获取判断字符串是否以python
开头,是否以python
结尾。
对于这种情况,之前匹配方法就有点不够用了,因此在正则表达式中 用^
可以表示开始,用 $
表示结束,示例如下:
a = re.search(r'^python', 'python is easy')b = re.search(r'python$', 'python is easy')c = re.search(r'^python', 'i love python')d = re.search(r'python$', 'i love python')print(a.span())print(b)print(c)print(d.span())
执行输出结果如下:
(0, 6)NoneNone(7, 13)
可以发现,在上述例子中,python is easy
和i love python
都存在python
字符串,但是一个在开头一个在结尾,因此变量a
和变量d
都匹配到了信息。其他则无法匹配到信息。
通配符
在生活中我们经常会有这么一种场景,我们记得某个人名为孙x者,就是不记得他叫孙行者,在正则表达式中针对此类场景,产生了通配符的概念,用符号.
表示。它代表匹配任何单个字符,不过值得注意的是,它只能出现在方括号字符组以外。
值得注意的是:.
字符只有一个不能匹配的字符,也就是换行(\n
),不过让.
字符与换行符匹配也是可能的,以后会讨论。示例如下:
a = re.findall(r'p.th.n','hello python re')b = re.findall(r'p.....','学好 python 人见人爱')print(a)print(b)
输出:
['python']['python']
可选字符
到目前为止,我们看到的正则表达式都是在正则表达式中的字符与被搜索的字符串中的字符保持1:1
的关系。
不过有时,我们可能想要匹配一个单词的不同写法,比如color
和colour
,或者honor
与honour
。
这个时候我们可以使用 ?
符号指定一个字符、字符组或其他基本单元可选,这意味着正则表达式引擎将会期望该字符出现零次或一次。
a = re.search(r'honou?r','He Served with honor and distinction')b = re.search(r'honou?r','He Served with honour and distinction')c = re.search(r'honou?r','He Served with honou and distinction')print(a)print(b)print(c)
执行结果输出如下:
<re.Match object; span=(15, 20), match='honor'><re.Match object; span=(15, 21), match='honour'>None
可以发现,在上述三个例子中,正则表达式为honou?r
,这里可以匹配的是 honor
和 honour
不能匹配 honou
,可以知道的是 ?
确定了前一个u
是可选的。在第一个示例中,没有u
,是没有问题可以匹配的;在第二个示例中,u
存在这也没有问题;在第三个例子中,u
存在但是r
不存在,这样就不能匹配了。
重复
到目前为止,我们只是学习了关于仅出现一次的字符串匹配,在实际开发过程中,这样肯定不能满足需求,比如要匹配电话号码,比如匹配身份证号,这些都是很多个数字组成的。
如果遇到这样的情况,我们可能期望一个字符组连续匹配好几次。
在正则表达式在一个字符组后加上{N}
就可以表示{N}
之前的字符组出现N
次。举个例子:
a = re.findall(r'[\d]{4}-[\d]{7}','张三:0731-8825951,李四:0733-8794561')print(a)
输出为: ['0731-8825951', '0733-8794561']
重复区间
可能有时候,我们不知道具体匹配字符组要重复的次数,比如身份证有15
位也有18
位的。
这里重复区间就可以出场了,语法:{M,N}
,M
是下界而N
是上界。
举个例子:
a = re.search(r'[\d]{3,4}','0731')b = re.search(r'[\d]{3,4}','073')print(a)print(b)
执行结果输出如下:
<re.Match object; span=(0, 4), match='0731'><re.Match object; span=(0, 3), match='073'>
通过上述代码,我们发现[\d]{3,4}
既可以匹配3
个数字也可以匹配4
个数字,不过当有4
个数字的时候,优先匹配的是4
个数字,这是因为正则表达式默认是贪婪模式,即尽可能的匹配更多字符,而要使用非贪婪模式,我们要在表达式后面加上 ?
号。
a = re.search(r'[\d]{3,4}?','0731')b = re.search(r'[\d]{3,4}?','073')print(a)print(b)
执行结果输出如下:
<re.Match object; span=(0, 3), match='073'><re.Match object; span=(0, 3), match='073'>
值得注意的是,上述代码这样子使用就只能匹配3
个数字而无法匹配4
个了。
开闭区间
在实际生活中,我们经常会遇到一种场景,我们知道此处会填写什么格式,但是我们不确定填写的内容。比如说每月支出,我们知道此处一定是数字,但是不确定这个月支出了多少钱,是3
位数,还是4
位数,说不定这个月就花了10个亿
。这时候我们可以用开区间来表示此范围,如下所示:
a = re.search(r'[\d]{1,}','我这个月花了:5元')print(a)
输出为: <re.Match object; span=(7, 8), match='5'>
速写
在正则表达式中,我们可以通过开闭区间来应对此种重复次数没有边界的场景。但是如此常见的需求,为什么不简单一点用一个符号表示出来了,每次都这样写不累么?是的,不仅累而且影响阅读,因此在正则表达式中,推出了2
个符号:
符号 | 含义 |
‘+’ | 重复匹配1个或多个 |
‘*’ | 重复匹配0个或多个 |
重复符号 +
符号+
用来表示重复一次到无数次,如下示范:
a = re.findall(r'[\d]+','0731-8859456')print(a)
执行结果输出如下:
['0731', '8859456']
重复符号 *
符号'*'
用来表示重复**0
次到无数次**,如下示范:
a = re.findall(r'[\d]*','0731-8859456')print(a)
执行结果输出如下:
['0731', '', '8859456', '']
为什么这一次的输出多了两个''
? 因为在匹配-
与末尾的字符时,没有匹配到一个数字,但是我们匹配到了0
个数字,因此输出了空的字符串''
。