python 正则表达式

re 模块

分析日志,提取信息

普通字符


原字符:

.        代表任意一个字符

^       以什么开头 ,  ^[abc]表示以abc开头

$       以什么结尾

*       匹配前面的字符或表达式0次或多次 0,1,2 ……

+      匹配前面字符或表达式至少1次         1,2,3 ……

?      匹配前面字符或表达式0次或1次        0,1

{}     里面可以是数值,{123}作为一个整体,可以匹配该整体几次

[]     匹配里面的某个字符,里面可以是个集合[0-9,a-z]

\       转意,\$,表示就是 $

|      表示或

()     表示分组



正则表达式

\d   匹配任何十进制数,相当于[0-9]

\D   匹配任何非十进制数,相当于[^0-9]

\s    匹配任何空白字符,相当于[\t\n\r\f\v]

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

\w   匹配任何字母和数字,相当于[a-zA-Z0-9]

\W   匹配任何非字母数字字符,相当于[^a-zA-Z0-9]




pattern:正则表达式,string:字符串  flag:标记,默认是0,返回的是一个列表
In [1]: import re
In [2]: help(re.findall)
Help on function findall in module re:
findall(pattern, string, flags=0)
    Return a list of all non-overlapping matches in the string.
    
    If one or more groups are present in the pattern, return a
    list of groups; this will be a list of tuples if the pattern
    has more than one group.
    
    
In [1]: import re

In [2]: help(re.findall)

#匹配以ab开头的行,没有找到
In [3]: re.findall(r'^ab+', 'asdabbbb')   
Out[3]: []

#匹配包含ab,b可以有多个,匹配到abbbb
In [4]: re.findall(r'ab+', 'asdabbbb')    
Out[4]: ['abbbb']

#匹配以ab开头的字符,ab
In [5]: re.findall(r'^ab+', 'absdabbbb')  
Out[5]: ['ab']

#匹配包含字符ab,匹配结果ab,abbbb
In [6]: re.findall(r'ab+', 'absdabbbb')   
Out[6]: ['ab', 'abbbb']

# .表示匹配任意一个字符  
In [7]: re.findall(r".",'abc\nab\n')      
Out[7]: ['a', 'b', 'c', 'a', 'b']

# re.S表示“.”匹配任意一个字符,包括换行符
In [8]: re.findall(r".",'abc\nab\n',re.S) 
Out[8]: ['a', 'b', 'c', '\n', 'a', 'b', '\n']

# abc$表示以abc结尾的字符
In [10]: re.findall(r'abc$','12345abc')
Out[10]: ['abc']

# ab*表示:b是0次或多次,匹配ab或a或abbbb    个数b>=0  ab* = ab{0,}      *0次或多次
In [17]: re.findall(r'ab*','abcea')
Out[17]: ['ab', 'a']
In [22]: re.findall(r'ab*','abceabbbba')
Out[22]: ['ab', 'abbbb', 'a']

# ab+表示:b是1次或多次,匹配ab或则abbbb     个数b>=1  ab+ = ab{1,}      +1次或多次
In [18]: re.findall(r'ab+','abcea')
Out[18]: ['ab']
In [19]: re.findall(r'ab+','abceabbbb')
Out[19]: ['ab', 'abbbb']

# ab?表示:b是0次或1次,匹配ab或则a          个数b=0或=1  ab? = ab{0,1}  ?0次或1次
In [20]: re.findall(r'ab?','abceabbbba')
Out[20]: ['ab', 'ab', 'a']


# ab{0,}=ab+表示: b是0次或多次,          个数b>=0
In [29]: re.findall(r'ab{0,}','abceabbbbcdefabbbbbbbbbbbbbbbbbbbb')
Out[29]: ['ab', 'abbbb', 'abbbbbbbbbbbbbbbbbbbb']


# ab{0,}表示: b是0-10次,                 个数b  0<=b<=10
In [30]: re.findall(r'ab{0,10}','abceabbbbcdefabbbbbbbbbbbbbbbbbbbb')
Out[30]: ['ab', 'abbbb', 'abbbbbbbbbb']

# ab{0,}表示: b是0-1次,                 个数b  0<=b<=1
In [32]: re.findall(r'ab{0,1}','abceabbbbcdefa')
Out[32]: ['ab', 'ab', 'a']


#\d表示一个数字,匹配1,2,3,0
In [38]: re.findall(r'[\d]','abcea1230')
Out[38]: ['1', '2', '3', '0']


#\d表示一个字符,a-z表示任何一个小写字母,表示a-z任意一个字符
In [33]: re.findall(r'[\da-z]','abcea')
Out[33]: ['a', 'b', 'c', 'e', 'a']

#\d表示一个字符,表示a-z任意一个字符任意数字
In [34]: re.findall(r'[\da-z]','abcea123')
Out[34]: ['a', 'b', 'c', 'e', 'a', '1', '2', '3']

#\d表示一个字符,表示a-z任意一个小写字母,+表示[\da-z]是一个整体,出现一次或多次
In [35]: re.findall(r'[\da-z]+','abcea123')
Out[35]: ['abcea123']

#\转译字符,\$表示美元符号$,而不是以abc结尾
In [40]: re.findall(r'abc$' , 'abc$')
Out[40]: []

In [41]: re.findall(r'abc\$' , 'abc$')
Out[41]: ['abc$']

#|表示或,包含a或b
In [45]: re.findall(r'a|b','abc')
Out[45]: ['a', 'b']

#不包含A或B
In [46]: re.findall(r'a|b','AB')
Out[46]: []

#使用re.I表示忽略大小写
In [48]: re.findall(r'a|b','AB',re.I)
Out[48]: ['A', 'B']

#\w表示匹配任意字母或数字,返回一个列表,()小括号表示分组,2个(\w+)表示匹配2个单词
In [52]: re.findall(r'(\w+) (\w+)', 'hello world')
Out[52]: [('hello', 'world')]

#\w表示匹配任意字母或数字,返回一个列表,()小括号表示分组,打印分组中的内容,2个(\w+)表示匹配2个单词
In [58]: re.findall(r'(\w+) abc (\w+)', 'hello abc world')
Out[58]: [('hello', 'world')]


一些方法

re.match() 方法:从左到右,返回一个对象,只匹配开始的位置

re.search() 方法:扫描字符串,找到这个re匹配的位置

re.match()和re.search()区别:

re.match只匹配字符串的开始,如果字符串不符合正则表达式,则匹配失败,函数返回none,

re.search匹配整个字符串,直到找到一个匹配,如果成功的话,返回MatchObject实例,例如,找到hello,返回hello

re.sub():替换



re.match() 方法:从左到右,返回一个对象,只匹配开始的位置,虽然包含hello,但是不在开头,所以匹配结果胃null

In [19]: m = re.match(r'(hello)\s', 'ab hello  world')

In [20]: m

In [21]: help(re.match)

Help on function match in module re:

match(pattern, string, flags=0)
    Try to apply the pattern at the start of the string, returning
    a match object, or None if no match was found.
    
#\w表示匹配任意数字和字母[a-zA-Z0-9],\s表示匹配任意空白字符:[\t\n\r\f\v]
In [24]: m = re.match(r'(\w)+\s', 'ab hello world')

In [25]: m
Out[25]: <_sre.SRE_Match at 0x7f2a186ebaf8>

In [26]: m.group()
Out[26]: 'ab ' #匹配结果是ab和空格


re.search() 方法:扫描字符串,找到这个re匹配的位置,匹配整行,即使不在开头位置

In [30]: s = re.search(r'hello', 'ab hello world')

In [31]: s
Out[31]: <_sre.SRE_Match at 0x7f2a1b30e7e8>

In [32]: s.group()
Out[32]: 'hello'


#\s 表示匹配hello后边的空格
In [33]: s = re.search(r'hello\s', 'ab hello world')

In [34]: s.group()
Out[34]: 'hello '


re.sub():替换  从string找pattern,用repl替换,count 替换几次

In [60]: help(re.sub)

Help on function sub in module re:

sub(pattern, repl, string, count=0, flags=0)
    Return the string obtained by replacing the leftmost
    non-overlapping occurrences of the pattern in string by the
    replacement repl.  repl can be either a string or a callable;
    if a string, backslash escapes in it are processed.  If it is
    a callable, it's passed the match object and must return
    a replacement string to be used.
    
#查找hellohe,使用HE替换he,默认都替换
In [68]: re.sub(r'he','HE','hellohe')
Out[68]: 'HElloHE'

#count=0表示都替换
In [69]: re.sub(r'he','HE','hellohe',count=0)
Out[69]: 'HElloHE'

#count=2表示使用HE替换he2次
In [70]: re.sub(r'he','HE','hellohe',count=2)
Out[70]: 'HElloHE'

#count=1表示使用HE替换he1次
In [71]: re.sub(r'he','HE','hellohe',count=1)
Out[71]: 'HEllohe'


正则表达式

compile()将正则表达式编译成对象,并用她们来进行配置

In [72]: import re

In [73]: p = re.compile('ab*')  # *表示b出现0次或多次

In [74]: p
Out[74]: re.compile(r'ab*')

In [75]: s = 'abc'

In [76]: p.findall(s)
Out[76]: ['ab']

In [89]: p.findall('abbbbb')
Out[89]: ['abbbbb']

In [79]: type(p)
Out[79]: _sre.SRE_Pattern

In [80]: p.       
            p.findall    p.groupindex p.pattern    p.split      
            p.finditer   p.groups     p.scanner    p.sub        
            p.flags      p.match      p.search     p.subn     

In [81]: p.findall('a')
Out[81]: ['a']

In [82]: p.findall('abcde')
Out[82]: ['ab']

In [83]: p.search('tj abc abd')
Out[83]: <_sre.SRE_Match at 0x7f2a1200ab28>

In [84]: s = p.search('tj abc abd')

In [85]: s.group()
Out[85]: 'ab'

In [90]: s = p.match('a bcdef')

In [91]: s.group()
Out[91]: 'a'

In [92]: s = p.match('abbbb bcdef')

In [93]: s.group()
Out[93]: 'abbbb'


In [77]: reg = r'ab*'

In [78]: re.findall(reg,s)
Out[78]: ['ab']


re.I :  不区分大小写

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

例如: 

In [104]: p = re.compile(r'G.d')
In [107]: p.findall('god')
Out[107]: []
In [108]: p.findall('God')
Out[108]: ['God']

In [111]: p = re.compile(r'G.d',re.I)

In [112]: p
Out[112]: re.compile(r'G.d', re.IGNORECASE)

In [113]: p.findall('god')
Out[113]: ['god']

In [114]: p.findall('God')
Out[114]: ['God']


In [115]: p = re.compile(r'G.d',re.I|re.S)

In [116]: p.findall('g\nd')
Out[116]: ['g\nd']

In [117]: p.findall('g\nD')
Out[117]: ['g\nD']

In [118]: p.findall('g\tD')
Out[118]: ['g\tD']


re.M:多行匹配

.代表任意一个字符foo.匹配了foo2,$表示以foo.结尾
In [7]: re.findall(r'foo.$', 'foo1\nfoo2\n')
Out[7]: ['foo2']

re.M表示\n为换行,除了foo2,foo1也匹配成功
In [8]: re.findall(r'foo.$', 'foo1\nfoo2\n',re.M)
Out[8]: ['foo1', 'foo2']

In [9]: re.findall(r'foo.$', 'foo1\nfoo2\n',re.MULTILINE)
Out[9]: ['foo1', 'foo2']

In [18]: data = open('/etc/passwd').read()

In [19]: p=re.compile(r'^ftp', re.M)

In [20]: p.findall(data)
Out[20]: ['ftp']

In [21]: p.search(data)
Out[21]: <_sre.SRE_Match at 0x7f3e657f5988>

In [22]: p.search(data).group()
Out[22]: 'ftp'

In [24]: data
Out[24]: 'root:x:0:0:root:/root:/bin/bash\nbin:x:1:1:bin:/bin:/sbin/nologin\ndaemon:x:2:2:daemon:/sbin:/sbin/nologin\nadm:x:3:4:adm:/var/adm:/sbin/nologin\nlp:x:4:7:lp:/var/spool/lpd:/sbin/nologin\nsync:x:5:0:sync:/sbin:/bin/sync\nshutdown:x:6:0:shutdown:/sbin:/sbin/shutdown\nhalt:x:7:0:halt:/sbin:/sbin/halt\nmail:x:8:12:mail:/var/spool/mail:/sbin/nologin\nuucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin\noperator:x:11:0:operator:/root:/sbin/nologin\ngames:x:12:100:games:/usr/games:/sbin/nologin\ngopher:x:13:30:gopher:/var/gopher:/sbin/nologin\nftp:x:14:50:FTP User:/var/ftp:/sbin/nologin\nnobody:x:99:99:Nobody:/:/sbin/nologin\ndbus:x:81:81:System message bus:/:/sbin/nologin\nvcsa:x:69:69:virtual console memory owner:/dev:/sbin/nologin\nsaslauth:x:499:76:Saslauthd user:/var/empty/saslauth:/sbin/nologin\npostfix:x:89:89::/var/spool/postfix:/sbin/nologin\nsshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin\nrpc:x:32:32:Rpcbind Daemon:/var/cache/rpcbind:/sbin/nologin\nrpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin\nnfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin\nradvd:x:75:75:radvd user:/:/sbin/nologin\nhaldaemon:x:68:68:HAL daemon:/:/sbin/nologin\nqemu:x:107:107:qemu user:/:/sbin/nologin\ndaixuan:x:500:500::/home/daixuan:/bin/bash\nmysql:x:501:501::/home/mysql:/sbin/nogin\nzabbix:x:498:497:Zabbix Monitoring System:/var/lib/zabbix:/sbin/nologin\nnagios:x:497:496::/var/spool/nagios:/sbin/nologin\nntp:x:38:38::/etc/ntp:/sbin/nologin\nnrpe:x:496:495:NRPE user for the NRPE service:/var/run/nrpe:/sbin/nologin\napache:x:48:48:Apache:/var/www:/sbin/nologin\nnginx:x:495:494:Nginx web server:/var/lib/nginx:/sbin/nologin\nmysql-proxy:x:494:493:MySQL-Proxy user:/:/sbin/nologin\nmailnull:x:47:47::/var/spool/mqueue:/sbin/nologin\nsmmsp:x:51:51::/var/spool/mqueue:/sbin/nologin\namos:x:502:502::/home/amos:/bin/bash\ncarbon:x:493:492:Carbon cache daemon:/var/lib/carbon:/sbin/nologin\nsquid:x:23:23::/var/spool/squid:/sbin/nologin\ndockerroot:x:492:491:Docker User:/var/lib/docker:/sbin/nologin\ngit:x:503:503::/home/git:/usr/bin/git-shell\nmemcached:x:491:490:Memcached daemon:/var/run/memcached:/sbin/nologin\ndaixuan123:x:504:504::/home/daixuan123:/bin/bash\nuser1:x:505:505::/home/user1:/bin/bash\n'



编译标志flags,re.X正则可以用多行来写

邮箱为例子

In [30]: rmail = r'[\w_-]+@[\w]+\.[a-zA-Z]{2,3}'

In [31]: smail = 'root@123.com'

In [32]: re.findall(rmail,smail)
Out[32]: ['root@123.com']

In [33]: re.findall(rmail,'david-dai@zamplus.com')
Out[33]: ['david-dai@zamplus.com']

#定义rmail使用多行来写,匹配时候使用re.X支持多行
In [34]: rmail = r"""[\w_-]+
    ...: @
    ...: [\w]+
    ...: \.
    ...: [a-z]{2,3}"""

In [35]: re.findall(rmail,smail)
Out[35]: []

In [36]: re.findall(rmail,smail, re.X)
Out[36]: ['root@123.com']

In [37]: re.findall(rmail, 'david-dai@zamplus.com', re.X)
Out[37]: ['david-dai@zamplus.com']

In [38]: re.findall(rmail, 'david-dai@zamplus.com')
Out[38]: []


注:

[...]中括号是匹配里面的某一个字符。
[...]+表示至少匹配中括号里某一个字符。
其实[\w_]+ 和 [\w]+效果是一样,\w里多包括'_'这个字符


greedy模式   ?阻止贪婪模式,默认是贪婪匹配,带问号的是最短匹配。只匹配标签:<h1> </h1>  

In [4]: ss = '<h1> hello world </h1>'

In [5]: re.findall(r'<.*>', ss)
Out[5]: ['<h1> hello world </h1>']

In [6]: re.findall(r'<.*?>', ss)
Out[6]: ['<h1>', '</h1>']


#匹配/var/log/messages

In [37]: reg_syslog = re.compile(r'\w+ \d+ [\d:]+ [\w\.]+ \w+(\[\d+\])?: .*')

In [38]: ss = 'Dec 28 07:47:53 daixuan auditd[1476]: Audit daemon rotating log files'

In [39]: s = reg_syslog.search(ss).group()

In [40]: s
Out[40]: 'Dec 28 07:47:53 daixuan auditd[1476]: Audit daemon rotating log files'


正则匹配的分组表达     (?P<name>  \w+)

In [55]: ss = 'Dec 28 07:47:53 daixuan auditd[1476]: Audit daemon rotating log files'

In [56]: reg_syslog = re.compile(r'(?P<logtime>\w+ \d+ [\d:]+) (?P<hostname>[\w\.]+) (?P<program>\w+(\[\d+\])?:) (?P<messages>.*)')
    ...: 

In [57]: s = reg_syslog.search(ss)

In [58]: sg = reg_syslog.search(ss).group()

In [59]: sg
Out[59]: 'Dec 28 07:47:53 daixuan auditd[1476]: Audit daemon rotating log files'

#得到字典
In [62]: s.groupdict()
Out[62]: 
{'hostname': 'daixuan',
 'logtime': 'Dec 28 07:47:53',
 'messages': 'Audit daemon rotating log files',
 'program': 'auditd[1476]:'}

#得到与元祖
In [63]: s.groups()
Out[63]: 
('Dec 28 07:47:53',
 'daixuan',
 'auditd[1476]:',
 '[1476]',
 'Audit daemon rotating log files')
 
 #每条日志间的空格也考虑进去了
 In [64]: reg_syslog = re.compile(r'(?P<logtime>\w+\s+\d+\s[\d:]+)\s(?P<hostname>[\w\.]+)\s(?P<program>\w+(\[\d+\])?:) (?P<messages>.*)')

In [65]: ss = 'Dec 28 07:47:53 daixuan auditd[1476]: Audit daemon rotating log files'

In [66]: sg = reg_syslog.search(ss).group()

In [67]: s.groupdict()
Out[67]: 
{'hostname': 'daixuan',
 'logtime': 'Dec 28 07:47:53',
 'messages': 'Audit daemon rotating log files',
 'program': 'auditd[1476]:'}

In [68]: s.groups()
Out[68]: 
('Dec 28 07:47:53',
 'daixuan',
 'auditd[1476]:',
 '[1476]',
 'Audit daemon rotating log files')


使用正则表达式获取IP,非常放方便实用

[root@133 systeminformation]# vim re_ip.py 

#!/usr/bin/env python
import re
from subprocess import Popen,PIPE

def getIfconfig():
    tuple_addr = ('lo','vir','vnet','em3','em4')
    p = Popen(['ifconfig'], stdout=PIPE)
    data = p.stdout.read().split('\n\n')
    return [i for i in data if i and not i.startswith(tuple_addr)]

def parseIfconfig(data):
    re_devname = re.compile(r'(br|em|docker)[\d:]+', re.M)
    re_mac = re.compile(r'HWaddr ([0-9A-F:]{17})', re.M)
    re_ip = re.compile(r'(inet addr):([\d\.]{7,15})', re.M)  #正则匹配(),group(1)代表匹配:(inet addr),group(2)代表匹配:([\d\.]{7,15})
    print re_ip.search(data).group(2)
    devname = re_devname.search(data)
    if devname:
        devname = devname.group()
    else:
        devname = ''
    mac = re_mac.search(data)
    if mac:
        mac = mac.group(1)
    else:
        mac = ''
    ip = re_ip.search(data)
    if ip:
        ip = ip.group(2)  #打印IP地址
    else:
        ip = ''
    return {devname: [ip, mac]}

if __name__ == '__main__':
    dic = {}
    data = getIfconfig()
    for i in data:
        dic.update(parseIfconfig(i))
    print dic
    
[root@133 systeminformation]# python re_ip.py 
112.65.140.133
172.17.42.1
192.168.101.237
{'docker0': ['172.17.42.1', '00:00:00:00:00:00'], 'br1': ['112.65.140.133', 'A4:BA:DB:20:93:23'], 'em2': ['192.168.101.237', 'A4:BA:DB:20:93:25']}


不是很明白这的group分组,数字怎么分的,以什么为分隔点

#按照括号分的,第一个括号是group(1),第二括号是group(2),以此类推。
#group(0)与group()相同,表示匹配到的内容。
In [35]: m = re.match(r'(\w+)\s(\w+)\s', 'hello world abc')

In [36]: m.group()
Out[36]: 'hello world '

In [37]: m.group(0)
Out[37]: 'hello world '

In [38]: m.group(1)
Out[38]: 'hello'

In [39]: m.group(2)
Out[39]: 'world'