正则表达式作为一名合格的程序员的必备的基本技术之一,其有用性不言而喻。但是它为什么会非常难以掌握,甚至想用一用也都感觉难以下手呢?本文将会让你一次就看会如何使用Python正则表达式。
在介绍如何使用Python的正则表达式时,我们需要先认识一下正则表达式的各种功能,以及其组成形式如何。
正则表达式可以从非结构化的文本中提取到我们想要的内容,其本质为模式匹配,也是体现出智能化的最初手段,现在已经广泛应用于自动化处理信息的流程之中,从爬虫到人工智能,无处不在,其需求也是相当的大。
一提及正则表达式的编写,在N多的博客里都提到了一个神奇的网站http://www.txt2re.com/,这个网站我之前也用过,如果你不会正则表达式,又想偷懒自动生成,你只需要在这个网站里复制粘贴一个最复杂的情况,然后把你想匹配的内容通过可视化的点击组合就可以自动生成你想要的正则表达式。
但是遗憾的是,这个网站目前已经打不开了。这也正是告诉我们,核心技术掌握在自己手里才是真啊。现在我们来看看正则表达式该如何编写。下面是使用进行正则表达式的一般流程,不同的语言其实现方法不完全相同,我们今天主要聚焦与使用python进行正则表达式的三种匹配方式,以获得我们想要的目标片段,对于其他方法,我们以后再进行讲解。
如果我们使用python进行一个正则表达式正则时,我们主要经历一下几个步骤:
1,3,5都是相对容易的部分,而其中最难的部分主要有两步,一个是制定一个符合需求的正则表达式,另一个则是如何进行匹配。我们最后会简单的介绍一下输出结果。
编写正则表达式是其中的核心,如何编写正确的,符合我们想法的表达式呢?我们这里介绍下面两个部分进行构建:
原封不动的单词
这一部分并不是我们需要的,只是一些留存在我们需要的内容中间的部分。原封不动的单词就原封不动的抄写上去,不要增加格外的形式。
待匹配的部分
这一部分使我们想抽取出的内容,将我们想匹配的部分使用正则表达式进行表达分为两个部分,一个部分为我们的匹配的字符,例如:
\w 匹配字母数字及下划线
\W 匹配非字母数字及下划线
\s 匹配任意空白字符,等价于 [\t\n\r\f].
\S 匹配任意非空字符
\d 匹配任意数字,等价于 [0-9].
\D 匹配任意非数字
\A 匹配字符串开始
\Z 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。
\z 匹配字符串结束
\G 匹配最后匹配完成的位置。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如, ‘er\b’ 可以匹配"never" 中的 ‘er’,但不能匹配 “verb” 中的 ‘er’。
\B 匹配非单词边界。‘er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。
\n, \t, 等. 匹配一个换行符。匹配一个制表符。等
\1…\9 匹配第n个分组的内容。
\10 匹配第n个分组的内容,如果它经匹配。否则指的是八进制字符码的表达式。
另一种,则是匹配的模式,决定我们如何进行匹配:
^ 匹配字符串的开头
$ 匹配字符串的末尾。
. 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。
[…] 用来表示一组字符,单独列出:[amk] 匹配 ‘a’,‘m’或’k’
[^…] 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。
re* 匹配0个或多个的表达式。
re+ 匹配1个或多个的表达式。
re? 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
re{ n} 精确匹配 n 个前面表达式。例如, o{2} 不能匹配 “Bob” 中的 “o”,但是能匹配 “food” 中的两个 o。
re{ n,} 匹配 n 个前面表达式。例如, o{2,} 不能匹配"Bob"中的"o",但能匹配 "foooood"中的所有 o。“o{1,}” 等价于 “o+”。“o{0,}” 则等价于 “o*”。
re{ n, m} 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
a| b 匹配a或b
(re) 对正则表达式分组并记住匹配的文本
(?imx) 正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。
(?-imx) 正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。
(?: re) 类似 (…), 但是不表示一个组
(?imx: re) 在括号中使用i, m, 或 x 可选标志
(?-imx: re) 在括号中不使用i, m, 或 x 可选标志
(?#…) 注释.
(?= re) 前向肯定界定符。如果所含正则表达式,以 … 表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩余部分还要尝试界定符的右边。
(?! re) 前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功
(?> re) 匹配的独立模式,省去回溯。
两者搭配即可完成我们想要的结果,虽然这里列举了很多,但是我们常用的就那几个,正则表达式的简单划分将正则表达式划分为元字符、反义、量词和懒惰限定词。我会在后面的部分给出一个实例。当我们的正则表达式撰写完毕后,我们使用下面的函数获得我们的匹配模板。
re.compile(pattern[, flags])
正则表达式匹配的方式主要有3种match
, search
和findall
。如果你懂英语的话,就知道它们的区别,这里前两个都是单一匹配,只会匹配一个流程,如果有多个符合匹配规则的,它们只会返回第一个结果,而findall
会把所有符合候选的都匹配出来。而前两个的区别就是match
必须是开头就要能够匹配,也就是和startwith
差不多的效果,而search
则可以在任意位置进行匹配。
下面看一下三个方法的参数表示,其中pattern
为我们制定的正则表达式,string
为我们要匹配的字符串,flags
表示匹配模式:
re.match(pattern, string, flags=0)
re.search(pattern, string, flags=0)
findall(string[, pos[, endpos]])
因此我们选择方式时有以下几个步骤:
findall
match
search
匹配结果展示主要有以下四个部分组成:
group([group1, …]) 方法用于获得一个或多个分组匹配的字符串,当要获得整个匹配的子串时,可直接使用 group() 或 group(0);
start([group]) 方法用于获取分组匹配的子串在整个字符串中的起始位置(子串第一个字符的索引),参数默认值为 0;
end([group]) 方法用于获取分组匹配的子串在整个字符串中的结束位置(子串最后一个字符的索引+1),参数默认值为 0;
span([group]) 方法返回 (start(group), end(group))。
例如下面这个例子,主要表现了我们如何调用这四个部分。
>>>import re
>>> pattern = re.compile(r'\d+') # 用于匹配至少一个数字
>>> m = pattern.match('one12twothree34four') # 查找头部,没有匹配
>>> print m
None
>>> m = pattern.match('one12twothree34four', 2, 10) # 从'e'的位置开始匹配,没有匹配
>>> print m
None
>>> m = pattern.match('one12twothree34four', 3, 10) # 从'1'的位置开始匹配,正好匹配
>>> print m # 返回一个 Match 对象
<_sre.SRE_Match object at 0x10a42aac0>
>>> m.group(0) # 可省略 0
'12'
>>> m.start(0) # 可省略 0
3
>>> m.end(0) # 可省略 0
5
>>> m.span(0) # 可省略 0
(3, 5)
一个更好的,更直观易懂的方法是如下这个例子,相比较刚才使用数字索引,它将每一个匹配内容语义化,使得代码更加容易理解。
contactInfo = ' ( Nucleus (span 2 3) (rel2par span)'
pattern=re.compile(r'(?P\w+) \(span (?P\w+) (?P\w+)\) \(rel2par (?P\w+)\)' )
match = pattern.search(contactInfo)
print(match.group()) # Nucleus (span 2 3) (rel2par span)
print(match.group("nuclearity")) # Nucleus
print(match.group("start")) # 2
print(match.group("end")) # 3
print(match.group("relation")) # span
从上述的例子中我们就可以获得最直观的结果,我们只需要将这些结果存入到我们需要的数据结构中即可。
如果我们只需要匹配一些常用的格式,如姓名、身份证、邮箱、电话号码等,都是有现成的工具直接生成,不需要我们进行再次编写。
如果你只是想匹配若干个中文汉字,使用下面的正则表达式:
[\u4E00-\u9FA5\\s]+ 多个汉字,包括空格
[\u4E00-\u9FA5]+ 多个汉字,不包括空格
[\u4E00-\u9FA5] 一个汉字
这里还有匹配更全的中文字的方法
正则表达式的各种括号的用处以及如何进行括号的匹配。