正则表达式(regular expression)主要功能是从字符串(string)中通过特定的模式(pattern),搜索想要找到的内容。
正则表达式的匹配过程是:依次拿出表达式和文本中的字符比较,如果每一个字符都能匹配,则匹配成功;一旦有匹配不成功的字符则匹配失败。
一、正则基础
1、元字符
字符 | 描述 |
. | 匹配出换行符以外的任意字符 |
w | 匹配字母数字或下划线或者汉字或者下划线 |
s | 匹配任意空白符相当于[ f v] |
d | 匹配数字 |
匹配单词边界,它只是匹配一个位置(不匹配字符) | |
^ | 匹配字符串开始(在多行模式中,匹配每一行开始) |
$ | 匹配字符串结束(在多行模式中匹配没一行结束) |
Tips: 是一个特殊代码,代表着单词的开头或结尾,也就是单词的分界处。虽然通常英文的单词是由空格,标点符号或者换行来分隔的,但是 并不匹配这些单词分隔字符中的任何一个,它只匹配一个位置。更精确的说法, 匹配这样的位置:它的前一个字符和后一个字符不全是(一个是,一个不是或不存在) w。
Tips:在字符组外表示单词边界,但是在字符组内表示退格。B表示非单词边界。
#匹配位置,新增字符
import re sre = r'(?<=d)(?=(?:d{3})+)' msg = 'there are 21931284028392 people' re.sub(sre,',',msg) #'there are 21,931,284,028,392 people'
#去掉指定关键前后的额外部分
msg1 = 'The cats is a dogor'
sre1 = r'(?<=(?:cat|dog))w+(?=)'
re.sub(sre1,'',msg1) #'The cat is a dog'
#把所有16进制转成0xNN形式
msg3 = '0x5 0xf3'
sre3 = r'(?<=0x)(?=[1-9a-zA-Z])'
re.sub(sre3, '0', msg3) #0x05 0xf3
#分割多种格式的字符串
line = 'asdf fjdk; afed, fjek,asdf, foo'
re.split(r'[;,s]s*', line) #['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo']
#当你使用re.split()
函数时候,需要特别注意的是正则表达式中是否包含一个括号捕获分组。 如果使用了捕获分组,那么被匹配的文本也将出现在结果列表中
fields = re.split(r'(;|,|s)s*', line) #['asdf', ' ', 'fjdk', ';', 'afed', ',', 'fjek', ',', 'asdf', ',', 'foo']
#如果你不想保留分割字符串到结果列表中去,但仍然需要使用到括号来分组正则表达式的话, 确保你的分组是非捕获分组,形如(?:...)
fields = re.split(r'(?:,|;|s)s*', line) #['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo']#匹配但是不捕获
例如: It's very nice.
'I' 占一个位置,'t' 占一个位置,所有的单个字符都会占一个位置,这样的位置我给它取个名字叫“显式位置”。
注意:字符与字符之间还有一个位置,例如 'I' 和 't' 之间就有一个位置(没有任何东西),这样的位置我给它取个名字叫“隐式位置”。
“隐式位置”就是 的关键!通俗的理解, 就是“隐式位置”。
nice 这里就能匹配出 "nice" 这个单词
反义字符
代码/语法 |
说明 |
W |
匹配任意一个不是字母或数字下划线或汉字的字符 |
S |
匹配任意一个不是空白符的字符 |
D |
匹配不是数字的字符 [^d] |
B |
匹配不是单词开头或者结尾的位置 |
A | 仅匹配开头 |
|
仅匹配末尾 |
[^X] |
匹配除了X以外的任意字符 |
[^aeiou] |
匹配除了aeiou这几个字母以外的任意字符 |
2、转移字符
如果你想要得到元字符本身的话需要使用“”来取消这些元字符的特殊意义
3、字符类
[]能够匹配所包含的一系列字符中的任意一个。需要注意的是,[]虽然能匹配其中的任意一个字符,但匹配的结果只能是一个字符,不是多个。
[u4e00-u9fa5]表示任意一个汉字
[^ ] 排除型字符组,[]本身表示的就是字符之间“或”的关系,因此在[]中使用“|”来表示“或”的关系是错误的。
3、重复
表达式 |
说明 |
举例 |
{m} |
表达式匹配m次 |
“d{3}”相当于“ddd ” “(abc){2}”相当于“abcabc” |
{m,n} |
表达式匹配最少m次,最多n次 |
“d{2,3}”可以匹配“12”或“321”等2到3位的数字 |
{m,} |
表达式至少匹配m次 |
“[a-z]{8,}”表示至少8位以上的字母 |
? |
表达式匹配0次或1次,相当于{0,1} |
“ab?”可以匹配“a”或“ab” |
* |
表达式匹配0次或任意多次,相当于{0,} |
“<[^>]*>”中“[^>]*”表示0个或任意多个不是“>”的字符 |
+ |
表达式匹配1次或意多次,至少1次,相当于{1,} |
“ds+d”表示两个数字中间,至少有一个以上的空白字符 |
4、分支结构
当一个字符串的某一子串具有多种可能时,采用分支结构来匹配,“|”表示多个子表达式之间“或”的关系,“|”是以()限定范围的,如果在“|”的左右两侧没有()来限定范围,那么它的作用范围即为“|”左右两侧整体。(正则表达式是从左到右依次匹配,如果满足了某个分支的话它就不会再管其他分支了)
二、正则进阶
1、捕获组
捕获组就是把正则表达式中子表达式匹配的内容,保存到内存中以数字编号或手动命名的组里,以供后面引用。
表达式 |
说明 |
(Expression) |
普通捕获,将子表达式Expression匹配的内容保存到以数字编号的组里 |
(?<name> Expression) |
命名捕获,将子表达式Expression匹配的内容保存到以name命名的组里 |
Tips:在.NET中使用(?’name’Expression)与使用(?<name>Expression)是等价的。在PHP和Python中命名捕获组语法为:(?P<name>Expression)
普通捕获:实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配。未命名编号是按“(”出现的顺序,从左到右,从1开始进行编号的 。(d{1,3}){3}d{3}:这个正则表达式的意思就是把我们分组的小括号里面的东西重复三次.group(2)---->valueError
命名捕获:通过捕获组名,而不是序号对捕获内容进行引用,提供了更便捷的引用方式,不用关注捕获组的序号,也不用担心表达式部分变更会导致引用错误的捕获组。
2、非捕获组
一些表达式中,不得不使用( ),但又不需要保存( )中子表达式匹配的内容,这时可以用非捕获组来抵消使用( )带来的副作用。
表达式 |
说明 |
(?:Expression) |
剥夺一个分组对组号分配的参与权 |
3、对捕获组的引用:
1) 正则表达式中,对前面捕获组捕获的内容进行引用,称为反向引用;
反向引用则用于重复搜索前面某个分组匹配的文本,例如1代表分组1匹配的文本
表达式 |
说明 |
k(number)简写1,2 |
对序号为1和2的捕获组的反向引用 |
k<name> |
对命名为name的捕获组的反向引用 |
2) 正则表达式中,(?(name)yes|no)的条件判断结构;
(?(?=a)w{2}|w) :当前位置右侧如果是字符“a” ,则匹配两个“w”,否则匹配一个“w”。
3) 在程序中,对捕获组捕获内容的引用。
4、零宽断言和负零宽断言
Tips:(如果子表达式匹配的仅仅是位置,或者匹配的内容并不保存到最终的匹配结果中,那么就认为这个子表达式是零宽度的。)
分类 |
代表/语法 |
说明 |
捕获
|
(exp) |
匹配exp,并捕获文本到自动命名的组里 |
(?<name>exp) |
匹配exp,并捕获文本到名称为name的组里,也可以写成(?’name’exp) |
|
(?:exp) |
匹配exp,不捕获匹配文本,也不给分组分配组号 |
|
断言
|
(?=exp) |
匹配exp前面位置,但是不匹配exp(顺序肯定环视) |
(?<exp) |
匹配exp后面位置,但是不匹配exp(逆序肯定环视) |
|
(?!exp) |
匹配后面的不是exp的位置,但是不匹配exp(顺序否定环视) |
|
(?<!exp) |
匹配前面不是exp的位置,但是不匹配exp(逆序否定环视) |
|
注释 |
(?#comment) |
注释 |
零宽度断言
1.(?=exp):也叫零宽度正预测先行断言,表示所在位置右侧能够匹配exp
例如:w+(?=ing)则这个正则表达式 就是匹配一ing结尾的单词,但是不包含ing,这个零宽度正预测先行断言可以这样理解,我们就以上面的正则表达式作为例来进行讲解,首先我们肯定是匹配 源文本为doing它会先匹配d的时候它会瞻仰一下后面跟的是不是ing,如果不是就会继续往下走,匹配到第二个字符o它会预测(或瞻仰)下后面是不是 ing如果是整个表达式就结束了,并且不匹配ing。而这个可以总结一句话就是匹配exp前面的东西
2.(?<=exp):也叫零宽度正回顾断言, 表示所在位置左侧能够匹配exp,这句话听着很绕口,其实零宽度正回顾断言中解释说是自身出现位置这个自身出现位置是表示它匹配的文本,就比如 说(?<=Ding)d{3}这个正则表达式,这里的自身出现的位置仅仅是从开始匹配文本的时候也就是d{3}也就是主动权在这个d{3}的 时候才是自身匹配的位置。举例说明源文本,比如匹配Din123,按照我们的常理理解的是数字123是自身匹配的位置,但是前面不是Ding所以匹配不成 功,我们可以讲这个表达式理解为就是以exp为开始的正则表达式但是不包含exp,意思就是匹配exp后面的东西。
负向零宽断言:(可以和上面的进行对比来学哦!这个表达式的是否定的)
1.(?!exp):也叫零宽度负预测先行断言,表示所在位置右侧不能够匹配exp
例如:d{3}(?!123):正则表达式的含义表达了前面匹配的是三个数字,匹配的位置就是当前匹配的这三个数字后面跟的不能是123。
2.(?<!exp):零宽度负回顾断言,表示所在位置左侧不能匹配exp。
5:正则引擎:
DFA(确定型有穷自动机)和NFA(非确定型有穷自动机),而NFA又可以分为传统型NFA和POSIX NFA。
DFA引擎不需要回溯,匹配快速。不能匹配具有反向引用的模式,还不可以捕获子表达式
正则的匹配过程,通常情况下都是由一个子表达式(可能为一个普通字符、元字符或元字符序列组成)取得控制权,从字符串的某一位置开始尝试匹配,一个子表达式开始尝试匹配的位置,是从前一子表达匹配成功的结束位置开始的。
6.匹配的贪婪与非贪婪模式
标准量词修饰的子表达式,在可匹配可不匹配的情况下,总会先尝试进行匹配,称这种方式为匹配优先,或者贪婪模式。“{m}”、“{m,n}”、“{m,}”、“?”、“*”和“+”都是匹配优先的。
一些NFA正则引擎支持忽略优先量词,也就是在标准量词后加一个“?”,在可匹配可不匹配的情况下,总会先忽略匹配,只有在由忽略优先量词修饰的子表达式,必须进行匹配才能使整个表达式匹配成功时,才会进行匹配,称这种方式为忽略优先,或者非贪婪模式。忽略优先量词包括“{m}?”、“{m,n}?”、“{m,}?”、“??”、“*?”和“+?”。
三: re模块
re.compile(strPattern[, flag]) #正则编译
re.I |
re.IGNORECASE | 忽略大小写 |
re.M | re.MULTILINE | 多行模式,改变'^'和'$'的行为 |
re.S | re.DOTALL | 点任意匹配模式(包括换行符),改变'.'的行为 |
re.L | re.LOCALE | 使预定字符类 w W B s S 取决于当前区域设定 |
re.U | re.UNICODE | 使预定字符类 w W B s S d D 取决于unicode定义的字符属性 |
re.X | VERBOSE | 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。 |
Match对象
*属性
string: 匹配时使用的文本
re: 匹配时使用的Pattern对象。
pos: 文本中正则表达式开始搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同。
endpos: 文本中正则表达式结束搜索的索引。值与Pattern.match()和Pattern.seach()方法的同名参数相同。
lastindex: 最后一个被捕获的分组在文本中的索引。如果没有被捕获的分组,将为None。
lastgroup: 最后一个被捕获的分组的别名。如果这个分组没有别名或者没有被捕获的分组,将为None
*方法:
res.group([group1, …]) #不填写参数时,返回group(0)
res.groups([default]): #所有
start([group]), end([group]), span([group]) #返回匹配在string的位置
match()和search() #返回Match对象
split(), findall() #返回匹配的子串将string分割后返回列表
finditer() #搜索string,返回一个顺序访问每一个匹配结果(Match对象)的迭代器。
re.sub(pattern, repl, string[, count]) #返回使用repl替换string中每一个匹配的子串后返回替换后的字符串。
re.sub(pattern, repl, string[, count]) #返回 (sub(repl, string[, count]), 替换次数)。