正则表达式

http://deerchao.net/tutorials/regex/regex.htm

 

关于匹配ip地址:

 

print(re.findall(r'(?:(?:25[0-4]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-4]|2[0-4]\d|[01]?\d\d?)',"200.1.26.199"))
有三个"?:" 分别在三个分组中,意思是这两个分组匹配的结果不显示,在这里达到的效果就是刚好显示整个IP"200.1.26.199"(分组不显示,显示整个表达式),如果不加这三个“?:”,结果会显示为:[('26.', '26', '199')]
 

 

 

re.findall()   匹配所有,结果返回为列表

元字符:^  $   *   +   ?   {}   []    |    ()    \

^开头匹配:

re.findall("^alex","alexahahaha")

$末尾匹配:

re.findall("alex$","testalex")

* 匹配0到多次

+ 匹配1到多次

?匹配0到1次

{} 匹配指定次数,可指定一个范围

[]字符集,字符集中到元素都是普通字符,即使是元字符也变成普通字符,但是有例外,比如表示范围:a-z, 另外^在字符集中表示排除,比如[^0-9]表示匹配所有不是0-9的字符。

re.findall("a[bc]d","string....") 会匹配:abd和acd。

findall默认有优先捕获,例如:re.findall(r"www.(baidu|sina).com","www.baidu.com") 显示结果为“baidu”,

去掉优先捕获显示“www.baidu.com”的方法是:re.findall(r"www.(?:baidu|sina).com","www.baidu.com")

 

反斜杠 \ :

\后面跟元字符,去除元字符的特殊作用

\后面跟普通字符,实现特殊功能

引用序号对应的字组所匹配的字符串(re.search(r"(alex)(eric)com\2"),"alexericcomeric")

 . 匹配任意字符

\d 匹配任何十进制数字,相当于类【0-9】

\D 匹配任何非数字字符,相当于类【^0-9】

\s 匹配任何空白字符,相当于类【\t\n\r\f\v】 

\S 匹配任何非空白字符,相当于类【^\t\n\r\f\v】

\w 匹配任何数字字母字符,相当于类【a-zA-Z 0-9】

\W 匹配任何非字母数字字符,相当于类【^z-zA-Z 0-9】

\b 匹配一个单词边界,也就指单词和空格间的位置。\b可放在匹配字符的前或后,在前表示字符前面有空格或其它非正常字符,在后则表示后面有空格或其它非正常字符:

例如:re.search("love\b", "i love you"),这个因为love前后都有空格,所以无论是love\b, \blove, \blove\b都可以匹配到love。

 

查找的三个函数:

findall()  查找所有的

search() 只查找第一个匹配的

match() 只查找开头

 

search函数的几个方法:

group( )  返回search匹配的值,默认参数0,返回所有匹配,可以将组号作为参数,返回相应的组,可接受多个参数,以,隔开,返回一个元组

start( )   返回匹配开始的位置

end( )    返回匹配结束的位置

span( )   返回一个元组包含匹配(开始、结束)的位置

 

改变re的工作模式

例:re.search("com","COM",re.I).group()  可以找到COM

re.I   使匹配对大小写不敏感

re.L  做本地化识别匹配

re.M 多行匹配,影响^和$

re.S  使. 匹配包括换行在内的所有字符

re.U  根据unicode字符集解析字符。

 

替换函数sub( )

例子:

re.sub("g.t","have","i get a, i got b,i gut c") 返回:i have a,i have b,i have c

re.sub("g.t","have","i get a, i got b,i gut c",1)返回:i have a,i got b,i gut c.替换了一次

函数subn( )同sub( )同样功能,但返回但是元组,并在元组中最后附加一个替换次数。 sub( )返回但是字符串。

 

compile( )函数:

例子:

import re

text = "JGood is a handsome boy, he is cool ,clever, and so young."

regex = re.compile(r"\w*oo\w*")

print(regex.findall(text))

返回['JGood','cool']

split( )分割函数:

re.split("\d+","one1tow2three3")

等同于:

p = re.compile(r"\d+")

p.split("one1two2three3")

注意:

re.split("\d+","oen1tow2three3")

输出:['one', 'tow', 'three', '']

re.split("(\d+)","one1tow2three3") 

输出:['one', '1', 'tow', '2', 'three', '3', '']

 

finditer( )函数:

w = re.finditer(r"\d+","12 durklaf44er dfdfaaf11.....10....")
for i in w:
print(i.group(),i.span())
12 (0, 2)
44 (10, 12)
11 (22, 24)
10 (29, 31)

 

 

 

Ip地址正则表达式

 

re.search(r"(([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5])\.){3}(25[0-5]|2[0-4]\d|[01]{0,1}\d{0,1}\d)","255.255.255.255")

 

匹配一个浮点或整数:

re.search("\d+\.\d*")

 

匹配一个电话号码:

re.search("\d{3,4}\-\d{8}","abc010-12345678aaa0532-88888888")

 

关于分组和group:

a = re.search("i.*","i love you") :

print(a.group()) : "i love you"

print(a.groups()) : ()

print(a.groupdict()) :{}

 
a = re.search("(i).*","i love you")

print(a.group()) : "i love you"

print(a.groups()) : ("i",)

print(a.groupdict()) :{}

 
a = re.search("(?P<name>i)(.*)","i love you")

print(a.group()) : "i love you"

print(a.groups()) : ("i","love you")

print(a.groupdict()) :{"name":"i"}

  

 

就是说在搜索时,所有匹配结果都会在group()中,加一个括号括起来匹配到部分会被放到groups里,在括号中前面加?P<value>, 会将括号中匹配到到内容作为value的值生成一个字典。

 

什么说的分组是search和March,但是分组对于findall却是有点区别的:

正常情况下:re.findall("a\w+","alex is a stupid alex"),匹配结果:【"alex","alex"】

加分组的花:re.findall("a(\w+)","alex is a stupid alex"),匹配结果:【"lex", "lex" 】

也就是说只取括号内的(组内的)内容

re.findall("(a)(\w+)","alex is a stupid alex"),匹配结果:【("a","lex"),("a","lex")】

re.findall("(a)(\w+(e))","alex is a stupid alex"),匹配结果:[('a', 'le', 'e'), ('a', 'le', 'e')],从左到右,从外到内匹配。

 

 

 

注意:

re.findall("(\w)(\w)(\w)(\w)","alex")

[('a', 'l', 'e', 'x')]

re.findall("\w{4}","alex")

['alex']

re.findall("(\w){4}","alex")

['x']

 

注意:

a = "1+(1+(1+1)+(2+2))"
n = re.findall("\([^()]*\)",a)
print(n) 
输出:['(1+1)', '(2+2)']
*号是重复次数,那么在这里重复的是([^()])的次数,最外层的\( \)是匹配(开头,以)结尾的部分,就是(1+(1+1)+(2+2)),然后([^()])表示匹配不包含括号的,双重条件加起来就是匹配以(开头,以)结尾,不包含()的部分,当匹配到(开头后,需要以)结尾,但是中间需要匹配一些东西,而([^()])其实就是匹配的内容(不包含()的部分),比如不包含括号的部分有八个数字,需要重复8次,所以需要加一个重复次数*或者+或者固定的{}。
 
下面是一个例子,循环去除表达式里的括号,然后计算结果:
import re
code = "1+2+(2*(3+4)+12*23+(1+(2*3)))"
def fun(x):
  a = eval(x)
  return a

while True:
  res = re.split("\(([^()]+)\)",code,1)#参数1是一次以一个匹配项分割,这样原本一个字符串就分割成了三个,在列表中就有三个项,所以len(res)=3
  if len(res) == 3:
    fornt = res[0]
    mid = res[1]
    bak = res[2]
    r = fun(mid)
    code = fornt+str(r)+bak
  else:
    print(fun(code))
    break

  

 
 
 
 

使用小括号的时候,还有很多特定用途的语法。下面列出了最常用的一些:

常用分组语法
分类 代码/语法 说明
捕获    (exp) 匹配exp,并捕获文本到自动命名的组里

(?<name>exp) 

{ python:  (?P<name>exp) }

匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp)
(?:exp) 匹配exp,不捕获匹配的文本,也不给此分组分配组号
零宽断言 (?=exp) 匹配exp前面的位置
(?<=exp) 匹配exp后面的位置
(?!exp) 匹配后面跟的不是exp的位置
(?<!exp) 匹配前面不是exp的位置
  (?#comment) 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读

 

1. 后向引用

  使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。

呃……其实,组号分配还不像我刚说得那么简单:

  • 分组0对应整个正则表达式
  • 实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号
  • 你可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权.

  后向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本。难以理解?请看示例:

  \b(\w+)\b\s+\1\b可以用来匹配重复的单词,像go go, 或者kitty kitty。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(\b(\w+)\b),这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符(\s+),最后是分组1中捕获的内容(也就是前面匹配的那个单词)(\1)。

In [65]: pattern = re.compile(r"\b(\w+)\b\s+\1\b")
In [66]: str = "hello go go hello"
In [67]: re.findall(pattern,str)
Out[67]: ['go']

你也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?<Word>\w+)(或者把尖括号换成'也行:(?'Word'\w+)),这样就把\w+的组名指定为Word了。要反向引用这个分组捕获的内容,你可以使用\k<Word>,所以上一个例子也可以写成这样:\b(?<Word>\w+)\b\s+\k<Word>\b。

  在python中指定子表示式的格式为: (?P<name>...),向后引用的格式为: (?P=name),所以上述所说的正则表达式在ptyon中正确表达为: r"\b(?P<word>\w+)\b\s+(?P=word)\b"。实例如下:

In [86]: str = "hello go go hello"
In [87]: pattern = re.compile(r"\b(?P<word>\w+)\b\s+(?P=word)\b")
In [88]: re.findall(pattern,str)
Out[88]: ['go']

 

2. 零宽断言

  接下来的四个用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。最好还是拿例子来说明吧:

  断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。

  (?=exp)也叫零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.时,它会匹配sing和danc。

In [100]: str = "I'm singing while you're dancing."
In [101]: pattern = re.compile(r"\b\w+(?=ing\b)")
In [102]: re.findall(pattern,str)
Out[102]: ['sing', 'danc']

(?<=exp)也叫零宽度正回顾后发断言(- _ - positive lookbehind assertion),它断言自身出现的位置的前面能匹配表达式exp。比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。

In [103]: str = "reading a book"
In [104]: pattern = re.compile(r"(?<=\bre)\w+\b")
In [105]: re.findall(pattern,str)
Out[105]: ['ading']
  print(re.findall(r"\w+(?=a)","sing and dancing and fucking"))
  输出:d
print(re.findall(r"\w+(?<=a)","sing and dancing and fucking"))
输出:['a', 'da', 'a']

下面这个例子同时使用了这两种断言:(?<=\s)\d+(?=\s)匹配以逗号间隔的数字(再次强调,不包括这些逗号)。

In [122]: str = "123,456,789"
In [123]: pattern = re.compile(r"(?<=,)?(\d+)(?=,)?")
In [124]: re.findall(pattern,str)
Out[124]: ['123', '456', '789']


3. 负向零宽断言

  前面我们提到过怎么查找不是某个字符或不在某个字符类里的字符的方法(反义)。但是如果我们只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?例如,如果我们想查找这样的单词--它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样: \b\w*q[^u]\w*\b,匹配包含后面不是字母u的字母q的单词。但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像Iraq,Benq,这个表达式就会出错。这是因为[^u]总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的[^u]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\w*\b将会匹配下一个单词,于是\b\w*q[^u]\w*\b就能匹配整个Iraq fighting。负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:\b\w*q(?!u)\w*\b。

  零宽度负预测先行断言(?!exp),断言此位置的后面不能匹配表达式exp。例如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字;\b((?!abc)\w)+\b匹配不包含连续字符串abc的单词。

同理,我们可以用(?<!exp),零宽度负回顾后发断言来断言此位置的前面不能匹配表达式exp:(?<![a-z])\d{7}匹配前面不是小写字母的七位数字。

  请详细分析表达式(?<=<(\w+)>).*(?=<\/\1>),这个表达式最能表现零宽断言的真正用途。

  一个更复杂的例子:(?<=<(\w+)>).*(?=<\/\1>)匹配不包含属性的简单HTML标签内里的内容。(?<=<(\w+)>)指定了这样的前缀:被尖括号括起来的单词(比如可能是<span>),然后是.*(任意的字符串),最后是一个后缀(?=<\/\1>)。注意后缀里的\/,它用到了前面提过的字符转义;\1则是一个反向引用,引用的正是捕获的第一组,前面的(\w+)匹配的内容,这样如果前缀实际上是<span>的话,后缀就是</span>了。整个表达式匹配的是<span>和</span>之间的内容(再次提醒,不包括前缀和后缀本身)。

  但是在python中这样做会报 look-behind requires fixed-width pattern 的错误,以也就是在负向零宽断言中不能有不确定长度的表达式:

In [128]: str = "<span> hello world </span>"

In [129]: pattern = re.compile(r"(?<=(<\w+>))(.*)(?=<\/\1>)")
---------------------------------------------------------------------------
error                                     Traceback (most recent call last)
......
error: look-behind requires fixed-width pattern

为了能够正确匹配需要将\w+改为\w{4}

In [130]: pattern = re.compile(r"(?<=<(\w{4})>)(.*)(?=<\/\1>)")
In [131]: re.findall(pattern,str)
Out[131]: [('span', ' hello world ')]
posted @ 2018-07-09 18:51  greenfan  阅读(138)  评论(0)    收藏  举报