Fork me on GitHub

关于正则表达式使用和总结

背景

正则表达式(regular Expression)是计算机中的概念。regular这里是规则、规律的意思,字面翻译指的是:规则的表达式。正则表达式主要用来处理字符串的工具,拥有自己独特的语法。 计算机常用语言均支持正则表达式,语法都是相同的,区别在于不同的语言支持的语法略有差异。

在数据科学实践中,特别是文本数据分析中,由于实际文本数据的纷繁复杂,正则表达式就成为文本检索分析的重要工具。本篇介绍以python语言背景来介绍正则表达式的实现。

https://jex.im/regulex/#!flags=&re=%5E(a%7Cb)*%3F%24

第一章 正则表达式字符匹配

正则表达式可以理解为一种字符串模式识别,识别对象有两个:子字符串、子字符串位置。

python中内置正则表达式包,直接import re。比如下面实现一个精确匹配:

1
2
3
4
import re
pattern = re.compile('hello')
print(pattern.findall('hello'))
# ['hello']

1.1 两种模糊匹配

正则表达式的强大主要实现模糊匹配,主要有:横向模糊匹配、纵向模糊匹配。

1、横向模糊匹配

定义:正则匹配的字符串的长度不是固定的。

举一个栗子:

我们需要匹配字符串中具有特有的模式子串:第一个字符是’a’,最后一个字符是‘c’,中间部分长度可变,但是均为’b’,数量范围为[2,3]。具体代码实现如下:

1
2
3
4
import re
pattern = re.compile('ab{2,3}c')
print(pattern.findall('abbcabbbbcaaabcaabbbc'))
# ['abbc', 'abbbc']

注意findall函数是全局匹配,即按顺序遍历字符串进行匹配。

2、纵向模糊匹配

定义:正则匹配的字符串,存在一些位置的值不固定。

举个栗子:

我们需要匹配字符串具有的模式为:第一个和最后一个字符为‘a’、’b’,中间部分长度为1,但是字符可选,备选集合为:{b,c,d} 。代码实现如下:

1
2
3
4
import re
pattern = re.compile('a[bcd]e')
print(pattern.findall('abeaaacdeade'))
# ['abe', 'ade']

正则表达式匹配的主要模式就是横向模糊、纵向模糊及两种模式的组合。

1.2 字符组

这里字符组其实匹配的是单个字符,并不是“组”哈。

范围表示方法

例如在纵向模糊中我们举的例子,用[b,c,d]表示这三个字符的其中一个。在实际中如果备选集合比较大怎么办?有简略的表达式,比如:

1
2
3
# [0,1,2,3,4,5,6,7,8,9]  等价表示为[0-9]
# [a,b,c,d,e,f,g,h] 等价表示为[a-h]
# 还可以各种类型字符合并写:[0-9a-h]

上面范围表达式,会被自动解析为连续字符。

有些杠精要问了假如备选集合中有横杆字符’-‘字符咋办,比如{‘a’,’-‘,’c’},这时候我们不能写写成这样了。要这样写:[-ac]、[ac-]、[a\-c],注意这里的转义符: ‘\’,后续我们再介绍。

排除字符组(反义字符组)

在纵向模糊匹配中,还有一种匹配模式:某个字符除了在排除集中的字符,可以是任意字符。

1
# 例如[^abc],表示一个除"a""b""c"之外的任意一个字符。字符组的第一位放^(脱字符),表示求反的概念。

最后我们介绍简写形式,即解析语义约定俗成的简短写法。

\d就是[0-9]。表示是一位数字。记忆方式:其英文是digit(数字)。

\D就是[^0-9]。表示除数字外的任意字符。

\w就是[0-9a-zA-Z_]。表示数字、大小写字母和下划线。记忆方式:w是word的简写,也称单词字符。

\W[^0-9a-zA-Z_]。非单词字符。

\s[ \t\v\n\r\f]。表示空白符,包括空格、水平制表符、垂直制表符、换行符、回车符、换页符。记忆方式:s是space character的首字母。

\S[^ \t\v\n\r\f]。 非空白符。

.就是[^\n\r\u2028\u2029]。通配符,表示几乎任意字符。换行符、回车符、行分隔符和段分隔符除外。

1.3 量词

简写形式

量词即连续重复的字符模式。

  • {m,n} 表示至少出现次数范围为:大于等于m,小于等于n。

  • {m,} 表示至少出现m次,即{m,+infinity}。{m} 等价于{m,m},表示出现m次。

  • ? 等价于{0,1},表示出现或者不出现。记忆方式:问号的意思表示,有吗?

  • +等价于{1,},表示出现至少一次,即{1,+infinity}。

  • * 等价于{0,},表示出现任意次,即{0,+infinity}。

贪婪匹配和惰性匹配

先看一个栗子:

1
2
3
4
import re
pattern = re.compile('\d{2,5}')
print(pattern.findall('123a1234b12345c123456d'))
# ['123', '1234', '12345', '12345']

上面的正则表达式会匹配连续出现的数值型字符串,长度范围为:2,3,4,5。这种模式是贪婪模式,即尽可能的匹配到最长范围。

而对应的由惰性匹配,即尽可能少的匹配:

1
2
3
4
import re
pattern = re.compile('\d{2,5}?')
print(pattern.findall('123a1234b12345c123456d'))
# ['12', '12', '34', '12', '34', '12', '34', '56']

量词组通过问号实现惰性匹配,还有其他情况:

{m,n}?

{m,}?

??

+?

?

记忆技巧:问号的含义是反问:还不知足吗?意思就是要惰性一点。。。。

1.4 多选分支

一个模式可以实现横向和纵向模糊匹配。而多选分支可以支持多个子模式任选其一。

具体形式如下:(p1|p2|p3),其中p1p2p3是子模式,用|(管道符)分隔,表示其中任何之一。

例如要匹配”good”和”nice”可以使用/good|nice/。测试如下:

1
2
3
4
import re
pattern = re.compile('goodby|good')
print(pattern.findall('googbygood'))
# ['good']

也就是说,分支结构也是惰性的,即当前面的匹配上了,后面的就不再尝试了。

1.5 案例分析

  • 匹配时间字符串

    例如需要匹配字符串中24小时制的时间字符串,比如:10:30,23:59

1
2
3
4
5
import re
# 错误:pattern = re.compile('\d{2}:\d{2}')
pattern = re.compile('[01]\d:[0-5]\d|2[0-3]:[0-5]\d')
print(pattern.findall('ahjs10:21s7:2sd23:59wi01:01iis09:02w'))
# ['10:21', '23:59', '01:01', '09:02']

第二章 正则表达式位置匹配

正则表达式属于模式匹配,主要两种匹配目标:字符和位置。

字符串的位置

位置定义:相邻字符之间的位置。

锚字符

Python中的锚字符

^(脱字符)和$(美元符)
  • ^(脱字符),用来匹配字符串开头。多行模式下,匹配每行的开头位置。

  • $(美元符),用来匹配字符串结尾。多行模式下,匹配每行的结尾位置。

举个栗子:

第三章 正则表达式括号的作用

第四章 正则表达式回溯原理

第五章 正则表达式的拆分

第六章 正则表达式的构建

第七章 正则表达式的性能调优

第八章 Python的正则表达式处理函数

写在最后

参考文献

1、

0%