正则表达式(Regular Expression)是强大、便捷、高效的文本处理工具。
本文是《精通正则表达式》的读书笔记和以前学正则时候的笔记汇总,一些概念性的东西本文并没有讲到,具体可以看看这本书。
正则引擎主要可以分为基本不同的两大类:一种是DFA(确定性有穷自动机),另一种是NFA(非确定性有穷自动 机)。正则引擎可以粗略地分为3类:DFA、传统型NFA、POSIX NFA,基本涵盖了现在主流的大部分程序,Python中的正则属于传统NFA型,本文中只讲Python中的正则。
NFA引擎最重要的性质是,它会依次处理各个子表达式或组成元素,遇到需要在两个可能成功的可能中进行选择的时候,它会选择其一,同时记住另一个,以备稍后可能的需要。
回溯就像是在道路的每个分岔口留下一小堆面包屑。如果走了死路,就可以照原路返回,直到遇见面包屑标示的尚未尝试过的道路。如果那条路也走不通,你可以继续返回,找到下一堆面包屑,如此重复,直到找到出路,或者走完所有没有尝试过的路。
总的来说DFA和NFA的明显区别之一在于效率,正如上面说到的,由于DFA没有回溯,因此看起来在某些情况下会比NFA来得更快,但是在真正使用 中,DFA需要进行预编译才能获得更好效果,因为DFA的匹配方式需要更多的内存和时间,
应用场景
- 特定规律字符串的查找替换切割等
- 邮箱格式、url等格式的验证
- 爬虫项目、提取特定的有效内容
- 很多应用的配置文件
基本使用
- 说明:正则是通过re模块提供支持的
相关函数:
- match:从开头进行匹配找到就立即返回正则结果对象,没有就返回None
serach:匹配全部内容,任意位置,只要找到,立即返回正则结果对象,没有就返回None
findall(pattern, string, flags=0),匹配所有内容,前两个返回的是对象,这个返回的是匹配结果组成的列表,没有返回None
flags有几种不同的意义,如下:
re.I (re.IGNORECASE): 忽略大小写
re.M(re.MULTILINE) :多行匹配,否则按照一行匹配
re.S :使.
可以匹配到\n
# 第一个参数是正则表达式,第二个参数是需要匹配的字符串 f = re.findall('abc', 'abcsdisuoiabcsjdklsjabc')
- compile:根据字符串生成正则表达式的对象,生成的是正则!用于特定正则匹配,通过match、search、findall匹配
# 根据字符串生成正则表达式的对象(需要匹配的字符串),用于正则匹配 c = re.compile('abc') # 然后进行特定正则匹配 m = c.search('abcdefghijklmn') # 分组后可以用m.group()来获取分组内容,不加参数是所有,加参数是获取指定
其实就是将re模块中的match、search、findall方法的处理过程分为了两步完成。
- re.sub(pattern, repl, string) 两个可选参数:count, flags
必选参数:
- pattern,表示正则中的模式字符串
【注】反斜杠加数字(\N),则对应着匹配的组(matched group)
比如\6,表示匹配前面pattern中的第6个group
意味着,pattern中,前面肯定是存在对应的,第6个group,然后你后面也才能去引用
- pattern,表示正则中的模式字符串
# 正则中的\1:表示前面第一个小括号匹配的结果
# 分组默认名称是\1,\2
# c = re.compile(r'<([a-z]+)><([a-z]+)>\w*</\2></\1>')
# c = re.compile(r'<(?P<hello>[a-z]+)><(?P<world>[a-z]+)>\w*</\2></\1>')
# 可以给指定的分组起个名字
c = re.compile(r'<(?P<hello>[a-z]+)><(?P<world>[a-z]+)>\w*</(?P=world)></(?P=hello)>')
s = c.search('xxx<div><a>百度一下</a></div>yyy')
if s:
print('ok')
print(s)
# group中也可以填参数,获取指定分组的匹配内容
print(s.group())
第二个参数:repl
repl,就是replacement,被替换的字符串的意思。
repl可以是字符串,也可以是函数。- 如果是字符串,其中的任何反斜杠转义字符,都会被处理的。
\n:会被处理为对应的换行符;
\r:会被处理为回车符;
其他不能识别的转移字符,则只是被识别为普通的字符:
比如\j,会被处理为j这个字母本身;
反斜杠加g以及中括号内一个名字,即 \g < name >,对应着命了名的组
第三个参数:string
string,即表示要被处理,要被替换的那个string字符串。- 如果是字符串,其中的任何反斜杠转义字符,都会被处理的。
正则表达式, 替换内容, 字符串
支持的元字符,以及这些元字符的意义,通常称为正则表达式的流派。
元字符
在理解正则的时候最好养成按照字符来理解正则表达式的习惯,比如 ^cat
,不要理解成匹配以cat开头的行,而要理解成匹配的是以c作为一行的第一个字符,紧接一个a,紧接一个t的文本,这两种理解的结果并没有差异,但按照字符来解读更易于明白遇到的正则表达式的内部逻辑。
脱字符号^
和美元符号 $
的特别之处在于它们匹配的是一个位置,而不是具体的文本,这种匹配位置的元字符也叫做锚点。
^cat$
文字意义:匹配的条件是开始是行开头,然后是字母cat,最后是行末尾三部分。
应用意义:只包含cat的行,没有多余的单词、空白字符。^$
文字意义:匹配的条件是,行开头和行末尾。
应用意义:空行(没有任何字符,包括空白字符)^
文字意义:匹配条件是行的开头
应用意义:无意义!因为每一行都有开头,每一行都能匹配到。
其余常见元字符:
. : 除换行以外所有字符
[] :[aoe] [a-w] 匹配集合中任意一个字符
\d :(digit)数字 [0-9]
\D : 非数字
\w :(word)数字、字母、下划线、中文
\W : 非\w
\s :(space)所有的空白字符
\S : 非空白
数量修饰:
* : 任意多次 >=0
+ : 至少1次 >=1
? : 可有可无 0次或者1次
{m} :固定m次
{m,} :至少m次
{m,n} :m~n次
边界:
\b \B
$ : 以某某结尾
^ : 以某某开头
- 分组匹配
|:表示或,具有最低的优先级
():用于表示一个整体,可以确定优先级,在python正则中也表示捕获,
会只返回捕获的内容,只用于分组的话可以用非捕获型括号(?: ),这时候括号就只用于分组了,
正则中的`\1`表示前面第一个小括号匹配的结果,以此类推
命名捕获:
python的引擎能为捕获的内容命名。Python的语法是(?P<name>...)
,如果要在正则表达式内部引用捕获的文本则使用(?P=name)
一些常用例子
匹配IP地址
分析:ip地址的每个字段都在0~255以内,且共有四个字段。完全匹配一个正确的ip地址的话,需要复杂的步骤,比如
^(?:[01]?\d\d?|2[0-4]\d|25[0-5])\.(?:[01]?\d\d?|2[0-4]\d|25[0-5])\.(?:[01]?\d\d?|2[0-4]\d|25[0-5])\.(?:[01]?\d\d?|2[0-4]\d|25[0-5])$
这样比较复杂,这个表达式只会匹配合法的ip地址,某些情况下,更合适的澡盆啊是不依赖正则表达式完成全部工作,比如可以用
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
,然后把几个数字变成程序中的变量,对这些变量再用其他程序进行验证。