一、为什么要学正则表达式
我们先不急着回答上面这个问题,我们来假设一个场景,我们打算在图书馆里面找一些有关于讲解TCP/IP的书籍,这时候如果我们不使用图书馆里面的电脑来进行查找的话,就得一本一本地找,但是当我们使用电脑时,就可以大概知道这类书籍在几层楼,第几个书架上,有没有被借走,这样是不是会很省事呢?而这里的电脑查找方式就是我们这篇文章的主题——正则表达式。
根据上面的例子,我们就可以认识到正则表达式的重要作用了,在软件开发或者办公的过程中,经常会涉及到大量的关键字等各种字符串的操作,我们就会使用正则表达式简化其复杂度和效率。
二、什么是正则表达式
正则表达式也叫做匹配模式、正规表示法、常规表示法,是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
在计算机科学中,是指一个用来描述或者匹配一系列符合某个模式或者规则的文本。而在很多文本编辑器或其他工具里,正则表达式通常被用来检索或替换那些符合某个模式或者规则的文本内容。许多程序设计语言都支持利用正则表达式进行字符串操作。
正则表达式通常缩写成regex,单数有regexp、regex,复数有 regexps、regexes、regexen
三、正则表达式历史
正则表达式的鼻祖可一直追溯到科学家对人类神经系统工作原理的早期研究。美国新泽西州的Warren McCulloch和出生在美国底特律的Walter Pitts这两位神经生理方面 的科学家,研究出了一种用数学方式来描述神经网络的新方法,他们创新地将神经系统中的 神经元描述成了小而简单的自动控制元,从而作出了一项伟大的工作革新。 在1956年,出生在被马克·吐温称为“美国最美丽的城市之一的”哈特福 德市的一位名叫Stephen Kleene的数学科学家,他在Warren McCulloch和Walter Pitts早期工作的基础之上,发表了一篇题目是《神经网事件的表示法》的论文,利用称之为正则集合的数学符号来描述此模型,引入了正则表达式的概念。正则表达式被作为用来描述其称之为正则集的代数的一种表达式,因而采用了正则表达式这个术语。 之后一段时间,人们发现可以将这一工作成果应用于其他方面。Ken Thompson就把这 一成果应用于计算搜索算法的一些早期研究,Ken Thompson是Unix的主要发明人,也就是大名鼎鼎的Unix之父。Unix之父将此符号系统引入编辑器QED,然后是Unix上的编辑器ed,并最终引入grep。Jeffrey Friedl在其著作《Mastering Regular Expressions》中对此作了进一步阐述讲解,自此以后,正则表达式被广泛地应用到各种UNIX或类似于UNIX的工具中,如大家熟知的Perl。
如果想深入了解正则表达式历史的读者可以阅读Jeffrey Friedl的著作《Mastering Regular Expressions》
四、语法
由于在不同场合正则表达式的语法也会有一些不同,这里我们以Python为例。
注意:以下这些符号都是放在函数的pattern
参数里面的
正则表达式符号区分大小写,大小写之间是互补关系
1、元符号
符号 | 描述 |
---|---|
. | 匹配除换行符\n 以外的任意字符 |
\w | 匹配大小写字母、数字、下划线、汉字和其他国家的语言符号 |
\W | 匹配非字母、数字、下划线、汉字和其他国家的语言符号 |
\s | 匹配任意的空白符 |
\S | 匹配任意非空白符 |
\d | 匹配数字 |
\D | 匹配非数字 |
[] | 匹配[] 中列举的字符,可以根据ASCII表,进行连用,a-z 表示a 到z 间 |
注意:上面符号除非有其它符号搭配,如:*
,否则都只匹配一次
2、数量
字符 | 功能 |
---|---|
* | 匹配前一个字符出现0次或者无限次,匹配到的字符可有可无 |
+ | 匹配前一个字符出现1次或者无限次,匹配到的字符至少有1次 |
? | 匹配前一个字符出现1次或者0次,匹配到的字符要么有1次,要么没有 |
{m} | 匹配前一个字符出现m次 |
{m,} | 匹配前一个字符至少出现m次 |
{m,n} | 匹配前一个字符出现从m到n次 |
注意:上面字符前面必须有元字符等字符,否则抛出异常
3、边界
字符 | 功能 |
---|---|
^ | 匹配字符串开头向后,后面可以有字符搭配,也可以没有 |
$ | 匹配字符串结尾向前,前面可以有字符搭配,也可以没有 |
\b | 匹配符合一个单词的所有边界,根据需要匹配的边界搭配字符,没有字符则匹配所有空边界 |
\B | 匹配符合非单词的所有边界 |
4、分组
字符 | 功能 |
---|---|
| | 匹配左右任意一个表达式 |
(ab) | 将括号中字符作为一个分组 |
\num | 引用分组num匹配到的字符串 |
(?P<name>) | 分组起别名 |
(?P=name) | 引用别名为name分组匹配到的字符串 |
有时需要查找不属于某个能简单定义的字符类的字符,那么就是需要反义。
前面的四个和上面一样,重点是第五个
字符 | 说明 |
---|---|
\W | 匹配任意不是字母,数字,下划线,汉字的字符 |
\S | 匹配任意不是空白符的字符 |
\D | 匹配任意非数字的字符 |
\B | 匹配不是单词开头或结束的位置 |
[^aeiou] | 匹配除了 aeiou 这几个字母以外的任意字符 |
在python中,如果我们需要对一个特殊字符进行转义(取消其特殊性),那么我们可以使用\
,但是遇到好几个\
要输出时,就需要用双倍的反斜杠对本身进行转义,这时就会非常麻烦,所以python提供了原生字符串,就是在正则表达式前面加r
。
#例子
import re
string = "d\\7787"
data = re.match(r'd\\7\d',string)#在正则表达式前面加`r`
print(data)
#注意:这里只对\\不转义,其他单斜杠特殊字符还是会转义的
五、Python的re模块
为了使用正则表达式,Python使用re模块进行操作。
1、属性
这些属性主要是用来改变re方法内的flags
参数,即指定匹配模式,默认值为0。
属性 | 别名 | 作用 | 备注 |
---|---|---|---|
ASCII | A | 使元字符\w ,\W ,\b ,\B ,\d ,\D ,\s 和\S 仅匹配ASCII字符 | 只在string 模式下有意义,在byte 模式下将被忽略 |
DEBUG | 显示debug信息 | ||
I | IGNORECASE | 忽略大小写匹配 | 对Unicode 字符同样生效,除非指定了re.ASCII 禁止匹配非ASCII字符 |
L | LOCALE | 让\w ,\W ,\b ,\B 和大小写的匹配取决于当前的语言环境 | 该标志只能与byte 模式一起使用 |
M | MULTILINE | 多行模式,改变元字符^ 和$ 的行为 | 默认^ 只匹配字符串开始,指定后还会匹配每行的开始;默认$ 只匹配字符串结尾,指定后还会匹配每行结尾 |
S | DOTALL | 元字符. 匹配任意字符,包括换行符 | |
X | VERBOSE | 冗余模式,可以在表达式中添加注释 | 在编译时会忽略多余的空格和注释 |
不建议使用L
属性,因为语言环境机制非常不可靠,它一次只能处理一种区域性,并且仅适用于8位语言环境。默认情况下,Python3中已为string
模式启用了Unicode
匹配,并且能够处理不同的语言环境/语言
2、类型及模块
目前还没涉及到,了解即可。
类型
error,Match,Pattern,RegexFlag,Scanner
模块
copyreg,enum,functools,sre_compile,sre_parse
3、常用方法
注意:下面方法的参数有以下三个
pattern
为匹配的正则表达式
string
为需要被匹配的文本
flags
为标志位,用于控制正则表达式的编译方式
a、compile方法
compile(pattern,flags=0)
方法是将正则表达式pattern编译成pattern对象,并返回一个re.Pattern
对象,其实个人感觉这个方法有时候很多余。比如在使用match方法时,可以不使用这个方法。
例子
import re
string = '15468'
prog = re.compile('.')
data = prog.match(string)
print(data)#<re.Match object; span=(0, 1), match='1'>
#等价于data=re.match(string)
b、escape方法
escape(string)
方法是可以对string
中所有可能被解释为正则运算符的字符进行转义,并返回一个str
对象。如果字符串很长且包含很多特殊技字符,而你又不想输入一大堆反斜杠,或者字符串来自于用户,且要用作正则表达式的一部分的时候,可以使用这个函数。
例子
import re
string = '154\t6\n8'
data = re.escape(string)
print(data)#154\ 6\
#8
c、findall方法
findall(pattern,string,flags=0)
方法是从左到右匹配string
中pattern
的所有匹配项,并以找到的顺序组成list
对象返回,若没有,则返回空列表。
例子
import re
string = '154685958'
data = re.findall('5',string)
print(data)#['5', '5', '5']
d、finditer方法
finditer(pattern,string,flags=0)
方法是与findall
方法一样,但是返回的是一个callable_iterator
对象,即迭代器对象。
例子
import re
string = '154685958'
data = re.finditer('5',string)
print(data)#<callable_iterator object at 0x000001BBCAF06760>
e、fullmatch方法
fullmatch(pattern,string,flags=0)
方法是判断string
与pattern
是否匹配,匹配则返回一个re.Match
对象,不匹配则返回None
。
例子
import re
string = '15468'
data = re.fullmatch('15468',string)
print(data)#<re.Match object; span=(0, 5), match='15468'>
f、match方法
match(pattern,string,flags=0)
方法是匹配以pattern正则表达式开头的字符串,并返回一个re.Match
对象实例,而不是在每个行的开头,若匹配不成功,则返回None。所以在使用正则表达式时,如果用$
是返回空值的。
例子
import re
string = '15468'
data = re.match('.5',string)
print(data)#<re.Match object; span=(0, 2), match='15'>
g、purge方法
re.purge()
方法是清除正则表达式缓存。
例子
import re
data = re.purge()
print(data)
h、search方法
search(pattern,string,flags=0)
方法是匹配string
中第一次出现pattern
中的表达式,并返回相应的re.Match
对象,若匹配不成功,则返回None。
例子
import re
string = '15468'
data = re.search('5',string)
print(data)#<re.Match object; span=(1, 2), match='5'>
i、split方法
split(pattern,string,maxsplit=0,flags=0)
方法是匹配到string中的pattern并将其拆分对应次数,并返回一个list
对象,若没有,则返回一个有整个string的列表。如果maxsplit不为零,则返回maxsplit数目+1。
例子
import re
string = '154648485'
data = re.split('4',string,1)
print(data)#['15', '648485']
j、sub方法
sub(pattern,repl,string,count=0,flags=0)
方法是先用匹配string
中的pattern
,然后将其转换成repl
,并返回一个str
对象,若没有,则返回原sting
。repl可以是字符串或函数,如果是字符串,则处理其中的任何反斜杠转义。count表示替换的次数。
例子
import re
string = '154648485'
data = re.sub('4','-',string,1)
print(data)#15-648485
k、subn方法
subn(pattern,repl, string,count=0,flags=0)
方法是与sub
方法一样,但返回一个由转换后的string
与次数组成的tuple
对象。
例子
import re
string = '154648485'
data = re.subn('4','-',string,2)
print(data)#('15-6-8485', 2)
l、template方法
template
方法是编译模板模式,返回一个re.Pattern
对象。
import re
data = re.template('.')
print(data)
4、贪婪模式
贪婪模式就是通过正则表达式尽可能多地匹配符合的字符,而在Python中正则表达式默认是贪婪模式(个别语言中也可能是非贪婪模式)。非贪婪模式则相反,尽可能少地匹配符合的字符。
如何将贪婪模式改成非贪婪模式呢?
那么我们可以在*
、?
、+
、{m,n}
等符号后面加上?
符号 | 说明 |
---|---|
*? | 重复任意次,但尽可能少重复 |
+? | 重复 1 次或更多次,但尽可能少重复 |
?? | 重复 0 次或 1 次,但尽可能少重复 |
{n,m}? | 重复 n 到 m 次,但尽可能少重复 |
{n,}? | 重复 n 次以上,但尽可能少重复 |
上面介绍的只是一些常用的,如果想深入了解可以查看官方文件:https://docs.python.org/3.6/library/re.html