一、正则表达式概述
1、什么是正则表达式
正则表达式,Regular Expression,缩写为regex或者RE。
正则表达式是一个很古老的技术了,早在1970年,就出现在Unix的文本编辑器中和现在依然经常使用的grep命令中,由于它对文本字符串的处理有着非常强大的检索功能,所以一直至今都在各种文本处理中都是使用正则表达式,哪怕随着时间的流逝,技术的快速更新发展,它仍然没有被淘汰。
正则表达式就是以某种规则对文本字符串进行匹配、检索、替换。
2、正则表达式的分类
RE:BRE,基本正则表达式,grep、sed、vi等软件支持
ERE:扩展正则表达式,egrep,grep -E等
PCRE:perl语言对RE的扩展,新增了很多特性,httpd工具采用了,所以为什么安装httpd工具,需要安装依赖pcre库。
二、正则表达式基本语法
1、元字符
元字符 |
说明 |
. | 匹配除了换行符之外的其他任意一个字符 |
[abc] |
字符集合,匹配集合中任意一个字符 |
[^abc] | 字符集合,匹配除了集合中任意一个字符 |
[a-z]、[0-9]、[A-Z] | 字符范围,匹配字符范围内的任意一个字符 |
[^a-z]、[^0-9]、[^A-Z] | 字符范围,匹配除了字符范围内的任意一个字符 |
\d | 匹配一位数字,等价于[0-9] |
\D |
匹配一个非数字,等价于[^0-9] |
\s | 匹配1位空白字符,包括换行符,制表符,空格 |
\S |
匹配1位非空白字符 |
\w | 匹配一个合法的标识符,字母、数字、下划线和中文 |
\W |
匹配1个特殊字符 |
2、位置控制字符
正则表达式 | 说明 | 举例 |
\b | 匹配单词的边界 | \bb:表示匹配b开头的单词 b\b:表示匹配b结尾的单词 |
\B | 不匹配单词的边界 |
t\B:表示不匹配以t结尾的单词,比如write |
^ | 表示行首位置 | – |
$ | 表示行尾位置 | – |
3、次数控制字符
正则表达式 | 说明 |
举例 |
* |
表示前面的1个字符可以重复任意次数 |
单词doogear用"o\w*"可以匹配oogear |
+ |
表示前面的1个字符最少出现一次 | 单词doogear用"o\w+"可以匹配oogear |
? |
表示前面的1个字符最多出现一次 | 单词doogear用"o\w?"可以匹配oo |
{n} |
表示前面的1个字符必须出现n次 | 单词doogear用"o\w{3}"可以匹配ooge |
{n,} | 表示前面的1个字符必须出现n次以上 | 单词doogear用"o\w{3,}"可以匹配oogear |
{n,m} | 表示前面的1个字符必须出现n次到m次之间 | 单词doogear用"o\w{3,5}"可以匹配oogea |
4、分组
正 则 表 达 式 | 说明 | 举例 |
pattern1|pattern2 | 匹配pattern1或者pattern2,如果pattern1或者pattern2都能匹配内容,则整个表达式的匹配结果是他们分别匹配的结果的合并。 | 单词doogear用"ary|doo"可以匹配doo。 单词doogear用"a|do\w{1}"可以匹配doo和a。 |
(pattern) |
使用小括号表示分组匹配,可以改变优先级,而且匹配的内容可以通过反向引用捕获,通过‘\数字’进行获取相应分组的内容。 | 句子"long long ago"用“(long) \1”可以匹配“long long”,第一个小括号(long)匹配第一个,\1表示小括号匹配到的内容 |
(?:pattern) | 仅仅是匹配内容和改变优先级,不能通过反向引用捕获内容 | – |
(?<name>pattern) 或 |
给分组起一个名字,然后可以通过分组名称的反向引用捕获内容,如‘\k<name>',在python中是使用(?P<name>pattern)进行分组 | 句子“long long ago”,可以用(?<tag>(long)?) \1或者(?<tag>(long)?) \k<tag>匹配“long long” |
5、断言
正则表达式 | 说明 | 举例 |
(?=pattern) | 表示断言后面一定会出现一个pattern能匹配的内容 |
单词“abcddeafaag”用‘a(?=bc)’匹配的结果是第一个‘a’字母。 |
(?<=pattern) | 表示断言前面一定会出现一个pattern能匹配的内容 | 单词“abcddeafaag”用‘(?<=de)a’匹配的结果是第二个‘a’字母。 |
(?!pattern) | 表示断言后面一定不能出现一个pattern能匹配的内容 | 单词“abcddeafaag”用‘a(?!bc)’匹配的结果是最后几个‘a’字母。 |
(?!<pattern) | 表示断言前面一定不能出现一个pattern能匹配的内容 | 单词“abcddeafaag”用‘(?<!de)a’匹配的结果是第一个和最后一个‘a’字母。 |
6、非贪婪模式
正则表达式 | 说明 |
*? |
表示前面的元字符可以重复任意次数,但尽可能的少重复 |
+? |
表示前面的元字符最少出现一次,但尽可能的少重复 |
?? |
表示前面的元字符最多出现一次,但尽可能的少重复 |
{n,}? | 表示前面的元字符必须出现n次以上,但尽可能的少重复 |
{n,m}? | 表示前面的元字符必须出现n次到m次之间,但尽可能的少重复 |
7、引擎选项
代码 | 说明 |
Python |
IgnoreCase |
匹配时忽略大小写 |
re.I |
SingleLine |
单行模式:整个字符串作为一个处理单独,‘.’可以匹配所有字符,包括换行符 | re.S |
MultiLine | 多行模式:每一行作为处理单位 | re.M |
IgnorePatternWhitespache |
忽略表达式中的空白字符 | re.X |
8、总结
① 1个元字符只是表示满足条件的1个字符,可以配合次数控制元字符来重复多次。
② 如果想使用元字符本身的意思,可以使用‘\’转义,比如‘\\d’表示‘\d’本身,不再表示一个数字。
③ 次数控制字符仅仅是重复前面一个字符或者元字符,不是重复前面所有字符。
④ 使用pattern1|pattern2,这整个正则表达式的匹配结果是pattern1和pattern2分别匹配的结果之和。
⑤ 分组的作用:匹配内容、改变优先级、反向引用
⑥ 分组的分类:普通分组,使用‘\数字’获取相应分组的内容,命名分组,使用‘\k<name>’获取相应分组的内容
⑦ 断言表达式一般是接在某个字符或者表达式的前面或者后面,相当于一个判断条件,本身断言不占分组号,也不会匹配结果。
⑧ 默认是贪婪模式,也就是尽可能的匹配,只要在重复的符号后面加一个‘?’,表示非贪婪,就尽量的少匹配。
⑨ 单行模式:把整个文本的内容作为一个长字符串,^、$分别表示这整个字符串的开始和结尾,‘.’也可以匹配所有字符
⑩ 多行模式:每一行作为处理单位,^、$分别表示这行的开始和结尾,‘.’也匹配除了换行符之外的所有字符
9、编写正则表达式的一般流程
① 使用元字符和普通字符把需要匹配的核心内容构造出一个表达式雏形。
② 再这个基础上,添加符合要求的位置控制和次数控制字符
③ 如果需要反向引用或者改变优先级,再添加分组
④ 最后再复杂的话,需要增加判断条件,再使用断言
三、python的正则表达式
1、re模块使用方法
在python中使用re模块来处理正则表达式,但是它有两种使用方法。
① 直接使用re模块的方法,如re.match()
② 事先编译一个正则表达式,通过返回的对象来调用,如regex.match()
2、编译:re.compile(pattern,flag=0) -> regex对象
说明:将指定的正则表达式编译到一个对象中,通过对象去操作正则表达式,被编译后,每次调用的时候就不需要再次编译,可以提高效率,而且使用这种方式,在进行匹配查找的时候,可以指定开始和结束位置。
① pattern:需要编译的正则表达式
② flag:编译标志位,有re.M、re.S、re.I、re.X可选,默认是re.S,如果需要多个可用‘|’符号开启,比如‘re.I|re.M’
3、match方法
① re.match(pattern,string,flags=0) ->match对象
② regex对象.match(string[,start[,stop]]) ->match对象
说明:re.match一定是从整个字符串的开头进行匹配,而regex对象.match可以指定开始和结束位置,如果找到第一个匹配则立即返回一个match对象,不会再关心后面的内容,如果找不到匹配内容,则返回一个None。
匹配的结果可以在match对象的方法中获取,有如下方法:
⑴ group([n[,m]]):如果为空或者‘0’,则表示获取整个正则表达式匹配的内容,如果为n,则明确表示获取第n个分组的内容,如果是n,m,则表示获取第n个到m个分组的内容。
⑵ groups():返回获取所有分组的内容组成的一个元组
⑶ group('name'):返回指定name的分组内容
⑷ groupdict():返回所有命名分组的内容组成的一个字典
⑸ start():返回匹配开始的位置
⑹ end():返回匹配结束的位置
>>> import re >>> s = """bottle\nbag\nbig\napple""" #使用re.match一定是从字符串的开头进行匹配,不管是单行还是多行模式,虽然字符串‘bag’在里面,但它不是在字符串的开头,所以匹配不到 >>> re.match('bag',s,re.M) None >>> re.match('bag',s,re.S) None #使用预先编译的方法,match可以指定开始位置 >>> regex = re.compile('bag') >>> regex.match(s,7).group() 'bag' #match不管是多行还是单行,依次是从字符串的开头开始匹配,而且找到第一个就不找了 >>> re.match('b',s,re.M).group() 'b' >>> re.match('b',s,re.S).group() 'b' #普通分组和group的使用方法 >>> re.match('(b)o(t+)l(e)',s,re.M).group() 'bottle' >>> re.match('(b)o(t+)l(e)',s,re.M).group(2) 'tt' >>> re.match('(b)o(t+)l(e)',s,re.M).group(2,3) ('tt', 'e') >>> re.match('(b)o(t+)l(e)',s,re.M).groups() ('b', 'tt', 'e') #命名分组,python的命名分组需要使用(?P<name>)格式 >>> re.match('(b)o(t+)l(?P<tag>e)',s,re.M).group(0) 'bottle' >>> re.match('(b)o(t+)l(?P<tag>e)',s,re.M).group('tag') 'e' >>> re.match('(b)o(t+)l(?P<tag>e)',s,re.M).groupdict() {'tag': 'e'}
4、search方法
① re.search(pattern,string,flags=0) -> match对象
② regex对象.search(string[,start[,stop]] -> match对象
说明:使用方法和match类似,只是search是从头开始搜索匹配内容,如果不匹配会继续往后搜索,直到搜索到第一个匹配为止返回match对象,如果搜索不到,则返回None
>>> import re >>> s = """bottle\nbag\nbig\napple""" #搜索到第一个字符‘le’之后,立即返回,不在匹配后面的内容 >>> re.search('le',s).group() 'le' #search和match一样,不管是单行还是多行模式,找到就返回 >>> re.search('^a',s,re.S).group() AttributeError: 'NoneType' object has no attribute 'group' >>> re.search('^a',s,re.M).group() 'a'
5、fullmatch方法
① re.fullmatch(pattern,string,flags=0) -> match对象
② regex对象.fullmatch(string[,start[,stop]] -> match对象
说明:python 3中新增的方法,使用方法和match类似,只是fullmatch是需要正则表达式可以字符串要完全匹配,如果匹配到了则返回match对象,如果匹配不到,则返回None
>>> re.fullmatch('bottle',s,re.M) None >>> re.fullmatch('bottle',s,re.S) None >>> regex = re.compile('bottle') >>> regex.fullmatch(s,0,6).group() 'bottle'
6、findall方法
① re.findall(pattern,string,flags=0) -> List
② regex对象.findall(string[,start[,stop]] -> List
说明:使用方法和match类似,findall会找到所有满足正则表达式匹配的内容,然后返回一个列表
#找到匹配内容 >>> re.findall('le',s) ['le', 'le'] #未找到匹配内容 >>> re.findall('lea',s) [] #单行模式,查找‘a’字母开头的 >>> re.findall('^a',s,re.S) [] #多行模式,查找‘a’字母开头的 >>> re.findall('^a',s,re.M) ['a']
7、finditer方法
① re.finditer(pattern,string,flags=0) -> Iterator
② regex对象.finditer(string[,start[,stop]] -> Iterator
说明:使用方法和功能跟findall类似,只是返回的结果不一样。finditer会找到所有满足正则表达式匹配的内容,然后返回一个迭代器,迭代器的每一个元素都是match对象
>>> iter = re.finditer('^a',s,re.M) >>> next(iter).group() 'a' >>> next(iter).group() StopIteration:
8、sub方法
① re.sub(pattern,replacement,string,count=0,flags=0) -> Str
② regex对象.sub(replacement,string,count=0) -> Str
说明:使用replacement的内容替换,pattern匹配到的内容
count:默认替换所有匹配到的内容,count可以指定替换次数
#默认所有匹配到的内容都将被替换 >>> re.sub('a','z',s) 'bottle\nbzg\nbig\nzpple' #指定替换次数 >>> re.sub('a','z',s,count=1) 'bottle\nbzg\nbig\napple'
9、subn方法
① re.subn(pattern,replacement,string,count=0,flags=0) -> Tuple
② regex对象.subn(replacement,string,count=0) -> Tuple
说明:使用方法和sub一样,只是返回的是一个由被替换后的新字符串和替换次数组成的二元组
>>> re.subn('a','z',s) ('bottle\nbzg\nbig\nzpple', 2) >>> re.subn('a','z',s,count=1) ('bottle\nbzg\nbig\napple', 1)
10、split方法
① re.split(pattern,string,maxsplit,flags=0) -> List
② regex对象.split(string,maxsplit) -> List
说明:按照指定正则表达式对字符串进行分割
#单行模式切割 >>> re.split('\s+',s,re.S) ['bottle', 'bag', 'big', 'apple'] #多行模式切割 >>> re.split('\s+',s,re.M) ['bottle', 'bag', 'big', 'apple']
四、re模块方法区别总结
匹配方法 | 说明 | 返回值 | 匹配次数 | 单行多行模式 |
match() | match只匹配整个字符串的开始,如果字符串开始不符合正则表达式,则匹配失败 | match对象 | 匹配到后立即返回 | 无区别 |
search | search从头开始匹配,如果开始不符合正则表达式,会继续往后进行搜索 | match对象 | 匹配到后立即返回 | 有区别 |
findall | 查找所有匹配到的内容 | 列表 | 匹配到后继续进行匹配 | 有区别 |