正则表达式
字符串是编程时遇到的最多的一种数据结构,比如判断一个电子邮件地址或一个座机号码是否符合格式要求,虽然我们可以提取 @
前后的字符串,再分别判断单词和域名,但这样代码十分复杂, 而且难以复用。
这时,我们可以
-
创建一个正则表达式
-
用这个正则表达式去检测字符串是否合法
正则表达式(Regulation Expression)是一种文本模式,包括普通字符(例如,a 到 z之间的字母,数字)和特殊字符(称为元字符,具有图书意义的字符,如 +
表示其前导字符至少出现1次,$
、*
、()
)
基本用法
正则表达式本身也是个字符串,怎么用字符描述字符呢?这就是我们这篇文章要讲的主要内容。
直接给出字符,就是精确匹配:
\d
表示一个数字
\w
表示一个字母或数字
.
表示任意字符,若要表示 .
英文句号,需要加转义字符 \.
因此:
00\d
可以匹配 003
、004
,但无法匹配 00A
\w\w\w
可以匹配 010
py.
可以匹配 pya
、py3
、py!
怎么匹配变长字符串:
-
*
表示任意个字符(包括0个),如runoo*b
可以匹配runob
、runoob
、runooob
-
+
表示至少出现一次 -
?
表示出现 0 次或 1 次 -
{n,m}
表示 n-m 个字符,注意:两个数字 n 和 m 之间不能有空格,只用逗号分隔
所以,\d{3}\s+\d{3,8}
可以匹配任意用空格隔开的带区号的电话号码
但是,以上方法匹配 010 - 2569888
还是不可以的,需要正则的高级用法
进阶
当有多种类型字符选择时,如数字、字母、大小写,可以用 []
来将其包括进去
-
[0-9a-zA-Z\_]
可以匹配一个数字、字母、下划线 -
[0-9a-zA-Z\_]+
可以匹配至少由一个数字、字母、下划线组成的字符串,如0558
、python343
-
[a-zA-Z\_][0-9a-zA-Z\_]*
可以匹配由字母或下划线开头的字符串,也是 python 合法变量格式 -
[a-zA-Z\_][0-9a-zA-Z\_]{1,19}
可以精确限制了字符串的长度是 1-20 -
A|B
匹配A
或B
,所以(P|p)ython
可以匹配python
或Python
-
^
表示行的开头,^\d
表示以数字开头 -
$
表示行尾,\w$
表示以字母结尾
^
、$
有什么作用,py
是可以匹配 python
的,但 ^py$
就只能匹配 py
了
python的 re
模块
python 内置了 re
正则模块,
现在尝试写一个正则表达式以验证 Email 地址:
Email 地址格式要求:
name@domain
name最长64,domain最长253,总长最长256
name可以使用任意ASCII字符:
-
大小写英文字母 a-z,A-Z
-
数字 0-9
-
字符 !#$%&'*+-/=?^_`{|}~
-
字符 .不能是第一个和最后一个,不能连续出现两次
但是有些邮件服务器会拒绝包含有特殊字符的邮件地址
domain仅限于26个英文字母、10个数字、连词号-
连词号-不能是第一个字符
顶级域名(com、cn等)长度为2到6个
代码如下:
>>> r = r'^[a-zA-Z]+[\.\_]?[a-zA-Z0-9]+@[a-zA-Z0-9]([\_]?[a-zA-Z0-9]+)*\.[a-zA-Z]{2,6}(\.[a-zA-Z]{2})?$'
>>> re.match(r, 'simonkindle@126.com')
<_sre.SRE_Match object; span=(0, 19), match='simonkindle@126.com'>
>>> re.match(r, 'simonkindle@126.com.cn')
<_sre.SRE_Match object; span=(0, 22), match='simonkindle@126.com.cn'>
>>> re.match(r, 'simonkindle@126.com.cn.cn')
贪婪匹配
*
、+
都是贪婪匹配的,即在符合格式要求的情况下尽可能多地匹配文字。如果想取消贪婪匹配,在它们后面加上一个 ?
即可。
具体看下面例子:
>>> todo = `<H1>Chapter 1 - 介绍正则表达式</H1>`
>>> r = '<.*>'
>>> re.match(r, todo)
<_sre.SRE_Match object; span=(0, 28), match='<H1>Chapter 1 - 介绍正则表达式</H1>'>
>>> r = '<.*?>'
>>> re.match(r, todo)
<_sre.SRE_Match object; span=(0, 4), match='<H1>'>
前者贪婪匹配,匹配 <
与 >
之间尽可能多的字符,后者非贪婪匹配,匹配 <
与 >
之间尽可能少的字符。
切分字符串
正则表达式还可以用来切分字符串,如何将以不定数空格分割的单词提取出来?
>>> s = 'a b fef v'
>>> s.split(' ')
['a', '', 'b', 'fef', '', '', 'v']
无法识别连续空格
>>> r = '[\s]+'
>>> re.split(r, s)
['a', 'b', 'fef', 'v']
分组
正则表达式还有提取子字符串的功能。用 ()
表示的就是要提取的分组(Group)。
>>> r = '^(\d{3})-(\d{3,8})$'
>>> m = re.match(r, '022-22820279')
>>> m.groups()
('010', '22820279')
>>> m[0]
'010-22820279'
>>> m[1]
'010'
>>> m[2]
'22820279'
>>> m[3]
group(0)
永远是原字符串,之后依次是括号中的字串
编译
当我们在 python 中使用正则表达式时,re 模块会在背后干这样两件事:
-
编译正则表达式,如果字符串本身不合法,会报错
-
用编译后的正则表达式去匹配字符串
如果我们使用的正则表达式要匹配上千次,每次都要编译会浪费大量时间。出于效率考虑,我们可以预编译该表达式,这样以后匹配就省去这个步骤了
>>> r = re.compile('^(\d{3})-(\d{3,8})$')
>>> r.match('010-22820279')
<_sre.SRE_Match object; span=(0, 12), match='010-22820279'>
>>> r.match('010-22820279').groups()
('010', '22820279')
网友评论