《精通正则表达式》学习笔记

一个重要且常见的问题:

写正则表达式时,我们需要在对欲检索的文本的了解程度和检索精准度之间求得平衡。: 越精准,容错性越差;但是容错性越好,越容易出现异常。所以,我们需要匹配所有需要匹配的,同时,忽略掉所有不需要匹配的。好像是句废话,实际是精髓。

一,正则表达式入门:

1.      字符组:

[123] : 1或2或3

[^123] :除123之外的任何字符,这里的字符是指广义字符。包括:~!@#$%^&*()等。

[^1] : 意义是:匹配一个除1之外的字符,而不是不要匹配字符1.

举例,请注意二者的差别:

re.findall('(f[^1])','asdfasdf')

['fa']

re.findall('(f[^1])','asdfasdf\n')

['fa', 'f\n']

[^-k] : 当”-”出现在[或[^之后,元字符”-”失效。

举例:

re.findall('([^-k])','dfdfdf')

['d', 'f', 'd', 'f', 'd', 'f']

2.      多选结构:“|”

‘123|456’ : 123或456

grey|gray : grey 或 gray

举例:

re.findall('grey|gray','graygrey')

['gray', 'grey'] : grey 或 gray

re.findall('gr[ea]y','graygrey')

['gray', 'grey'] : [ea]:e或a

re.findall('gre|ay','graygrey')

['ay', 'gre'] : 'gre’ 或 ’ay’

re.findall('(gr(?:e|a)y)','graygrey')

['gray', 'grey'] : (gr(?:e|a)y) 相当于gr(e|a)y :grey 或 gray

re.findall('gr[e|a]y','graygrey')

['gray', 'grey'] : [e|a] 相当于 e或”|”或a

re.findall('gr[e|a]y','graygreygr|y')

['gray', 'grey', 'gr|y']

注意re.compile(‘^a|b|c’)和re.compile(‘^(a|b|c)’)之间的差别。

前者指’^a’或’b’或’c’。

后者指’^a’或’^b’或’^c。

3.      正则中还有一个“单词锚定”的概念,但python似乎不支持,也许我没找到。

4.      量词? * +

re.compile(‘4(th)?’)等价于re.compile(‘4|4th’) 等价于re.compile(‘4(|th)’)。

re.compile(‘4(th)*’) 若干个th

5.      括号及反向引用

前文所述,括号有两个作用:

a)        re.compile(‘^(1|2|3)’) 针对多选项

b)        re.compile(‘4(th)?’) 针对量词

括号的第三个作用:反向引用

直接上例子:

re.search(r'(\d)abc\1','1abc1').groups()

('1',)

后面的\1匹配前面括号中匹配的东西”\d”. 有点绕口,在上几个例子:

re.search(r'(\d)abc\1','2abc2').groups()

('2',)

re.search(r'(\d)abc\1','1abc2').groups()

Traceback (most recentcall last):

  File "<interactive input>",line 1, in <module>

AttributeError: 'NoneType'object has no attribute 'groups'

没匹配上,出了异常,因为前面是1后面是2. '1abc2'

 

6.      python中反向引用的扩展:

re.compile(r'(?P<kk>\d)abc(?P=kk)').search('1abc1').groups()

('1',)

(?P<kk>\d):给这个group加一个名字<kk>

(?P=kk):获取已匹配的内容。

在来一个例子:

re.compile(r'(?P<asdf>\d)abc(?P=asdf)').search('1abc1').groups()

('1',)

在一个group里面的前两个字符是”?:”,那么这个括号不被正则引擎记入。。。有点绕后,希望这个例子能说明白。

re.compile(r'(?:\d)(\d)abc\1').search('12abc22').groups()

('2',)

例子中的\1 匹配的是第二个括号中的\d而不是第一个。因为第一个group中有一个”?:”被忽略掉了。

这个例子举的不好。因为这样也可以:

re.compile(r'\d(\d)abc\1').search('12abc22').groups()

('2',)

实际上”?:”这个东西主要用在上文中括号的第一和第二个作用(绑定/捆绑),又不想被记入第三个功能(反向引用)的时候使用的。。。希望我说明白了。 

小结:

 

二,扩展

1.      环视(lookaround

顺序环视 lookahead:

    肯定型顺序环视:

    否定性顺序环视

逆序环视:

    肯定型逆序环视

    否定性逆序环视:

这块儿有点烦。1,难。2,用的不多。3,但很重要。

举例之前,还是简单介绍一下吧。

前文曾经介绍过元字符^和&.

^表示 待匹配文本的开始

$表示 待匹配文本的结尾

像^,$这种元字符和perl中的\b,叫做锚定位。锚定就是锚一个位置,定住(废话!)。锚定位就是一个位置标识符,但是并不占用待匹配的内容。我举个例子:

re.compile('^qweqweqwe')

这句话的意思是说匹配以字母q开头,后面跟着weqweqwe。^只是一个位置标识符,表示起始位置,并不占用内容。

如果你想匹配” qweqweqwe”。正确。可匹配成功。

如果你想匹配”aqweqweqwe”.错误。因为开头是字母a 不是q。

不知道是否有同学会思考,如果我想匹配”^qweqweqwe”怎么办?就不告诉你~!

 

上段介绍了锚定的概念是为了引出这节将要介绍的环视。

其实环视我个人的理解就是一个变相的锚定。

举例子:

肯定型顺序环视:

re.search('(?=Linshuhao)Linsh','sjkjsLinshuhao')

<_sre.SRE_Match object at 0x029BA1E0>

匹配成功。

解释一下:

'(?=Linshuhao)Linsh' 这个是正则的定义。我们把它拆开:

(?=Linshuhao) 这个就是上文开头那个表中的第一项:肯定型顺序环视。

Linsh 这个就是想要去匹配的字符了。

'sjkjsLinshuhao' 这个是被匹配的文本。我要拿正则去匹配它。

 

放一起,解释一遍:

(?=Linshuhao)这个意思是锚定一个位置,哪个位置呢:'sjkjsLinshuhao' 就是我剪头指向的位置。

那么'(?=Linshuhao)Linsh 的意思就是在这个位置从左向右匹配Linsh。

举个反例:

re.search('Linsh','sjkjsLinshaahahahahha')== None

False

False意味着不等于None,意味着匹配成功了。

因为'sjkjsLinshaahahahahha'这个字符串中确实存在Linsh这个字符串。

re.search('(?=Linshuhao)Linsh','sjkjsLinshaahahahahha')== None

True

但是这个失败了。因为(?=Linshuhao),的意思是锚定位置到Linshuhao的起始位置。但是整个文本并没有这个位置。

 

在举一组例子:

记住,锚定位置!!!

re.search('123(?=Linshuhao)','sdakj123Linshuhao')== None

False

匹配成功。

两种理解方法,一个是官方的,一个是我的。

先说官方的:

首先锚定位置,(?=Linshuhao) 锚定的就是Linshuhao的前端,就是箭头所指的位置:

'sdakj123 Linshuhao'

 

 

'123(?=Linshuhao)' 整个放一起的意思就是123后面是那个锚定的位置。哪个锚定的位置?就是Linshuhao的起始位置。也就是箭头指向的那个位置。

 

我的理解:

'123(?=Linshuhao)'  123后面必须是Linshuhao,别的不匹配。

 

re.search('123(?=Linshuhao)','sdakj123fsdsaLinshuhao')== None

True

总结:(?=Jeffrey)Jeff 的匹配:

否定型顺序环视:

re.search('123(?=Linshuhao)','sdakj123Linshuhao')== None

False

还拿这个举例子

肯定型顺序环视是指123后面必须是Linshuhao。否定型的意思就是123后面必须不是Linshuhao。

否定型的表达式是这样的:

re.search('123(?!Linshuhao)','sdakj123Linshuhao')== None

True

re.search('123(?!Linshuhao)','sdakj123sdlLinshuhao')== None

False

自己感受下。。。

思考题:

re.search('123(?!=Linshuhao)','sdakj123Linshuhao')== None

True or False?

 

 

 

肯定型/否定型逆序环视:

下面重点介绍逆序环视:

re.search('(?<=Linshuhao)123','Linshuhao123')

<_sre.SRE_Match object at 0x029BA758>

 

匹配成功了。逆序环视匹配的是'Linshuhao123'

 

 

为了方便对比给出顺序环视的锚定位:

re.search('123 (?=Linshuhao) ', ‘123Linshuhao')

'123Linshuhao'

 

 

肯定型和否定型大家应该很清楚了。给一个否定型的写法吧。

re.search('(?<!Linshuhao)123','Linshuhao123')

 

环视的实际应用:

在《精通正则学习笔记1》中,我没有找到python支持的单词锚定元字符。在这里。我们可以应用环视,人为写出,相当于perl的\b的表达式:

1.表示字符的前端的锚定位

re.compile(r'(?<!\w)(?=\w)123')

<_sre.SRE_Pattern object at0x029D04D0>

2.表示字符的后端的锚定位

re.compile(r'123(?<=\w)(?!\w)')

<_sre.SRE_Pattern object at0x029C7340>

3.兼容前端和后端

re.compile(r'(?:(?<!\w)(?=\w))|(?:(?<=\w)(?!\w)'))

 

第二章,到此结束。

三,正则表达式的特性和流派概览

 

1.      正则模式和匹配模式

两种表达方式,各举一个例子:

a.      re.compile('123123',re.I|re.M)

b.      re.search('(?im)123123','123123123123123')

 

下面分别解释python支持的各个模式的意义:

re.I (ignore case):忽略大小写。

re.L (locale dependent):和re.U对应。一个是local 一个是unicode。本人猜测默认应该是local的。Unicode模式是指,unicode格式,两个字符连一起组成组合表示一个字符。从而影响到\w, \W,\b, \B, \d,\D, \s and \S。例如,\w可以匹配两个字符。猜测是这个意思。以后如果遇到,再说。

re.M (multi-line):多行模式,又叫增强的行锚点模式。呸呸呸。

^和$ 不在这个模式下,只匹配字符串的开始和结束位置。

但在多行模式下,^匹配的是一行的开始位置。同理$匹配一行的结束位置。

举例:

re.findall('(?m)^(\d{3})$','123\n456\n789')

['123', '456', '789']

re.findall('^(\d{3})$','123\n456\n789')

[]

自己体会一下。

多行匹配模式,是所有模式中应用最广泛的。

犹豫在该模式下,^和$只能匹配行首和行尾。字符串的首尾由元字符\A,\Z替换:

\A:指定匹配必须出现在字符串的开头(忽略  Multiline   选项)。

\Z:指定匹配必须出现在字符串的结尾(忽略  Multiline   选项)。

 

总结一下:

^:行首。&:行尾。\A:总字符串首。\Z:总字符串尾。

自己体会一下。

 

 

re.S (dot matches all):点号通配模式。

通常情况下“.”匹配所有字符,除了\n。

但是在点号通配模式下。”.” 匹配所有字符,包括\n.

举例:

这条语句曾经出现在driver management中的*common.py中,但是被扼杀了。

re.search('(?ms).*DeviceDesc%=.+?,(.*?)\s.*[Strings].*DeviceDesc\s*=\s*"(.*?)"\r\n',tmpString).groups()

 

 re.X (verbose):

回车换行,空格都无效。

下面两句话表示一个意思。

a = re.compile(r"""\d +  # the integral part

                  \.    # the decimal point

                  \d *  # some fractionaldigits""", re.X)

b = re.compile(r"\d+\.\d*")

 

2.      元字符+一些关键字总结:

a.      字符组:强调几点。

1.元字符在字符组内/外意义不同。也可能失效。例如[*]只代表”*”并不是量词。[\b]表示退格。\b表示。。。。

2.[^asdasd],字符组中的字符是指广义字符,包括且不限于@#$%^&*()_

3.排除型字符组[^asdasd]也表示仍然需要匹配一个字符。

b.      点号.:在大多数情况下,“.” == [^\n].在多行模式下“.”可以匹配任何字符(广义)。

c.      \d \D \w \W \s \S:自己百度。

d.      锚点: ^ $ \A \Z

e.      单词分解符 \b \B ,这里要解释一下,在《1》中曾经说python 不支持单词分解符,其实是支持的。

f.       环视 (?= … ) (?! …) (?<= …) (?<! …)

g.      (?: …) 只分组不捕获

h.      命名捕获:两种情况,

1,需成对出现:

re.search('((?P<qwe>\d))kk(?P=qwe)','1kk1').groups()

('1', '1')

re.search('(?P<qwe>\d)kk(?P=qwe)','1kk1').groups()

('1',)

re.search('(?P<qwe>\d)kk((?P=qwe))','1kk1').groups()

('1', '1')

re.search('(?P<qwe>\d)kk(?:(?P=qwe))','1kk1').groups()

('1',)

re.search('(?:(?P<qwe>\d))kk(?:(?P=qwe))','1kk1').groups()

('1',)

re.search('(?:(?P<qwe>\d))kk','1kk1').groups()

('1',)

2,单独出现亦可:

m = re.match(r"(?P<first_name>\w+)(?P<last_name>\w+)", "Malcolm Reynolds")

m.group('first_name')

'Malcolm'

m.group('last_name')

'Reynolds'

 

re.search('(?P<qwe>\d)kk','1kk1').groupdict()

{'qwe': '1'}

i.        固化分组,和正则引擎的机制有关(第四章介绍),吃进去的东西不会吐出来。

可惜,python不支持,但是可以通过环视模拟。nice!

'(?=(\w+))\1'

感受一下。

j.        多选结构或: “|”

k.      条件判断,针对正则表达式的条件判断:

re.search(r'(?P<tt>\d)asdasd(?(tt)\d|abc)','1asdasd1').groupdict()

{'tt': '1'}

re.search(r'(?P<tt>\d)?asdasd(?(tt)\d|)','rasdasdabc').groupdict()

{'tt': None}

re.search(r'(\d)asdasd(?(1)\d|abc)','1asdasd1').groups()

('1',)

l.        量词 ? * + {1,5}

m.    ?? *? +? {1,5} 量词的非贪婪模式。下章介绍。

 

第三章结束。

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值