python 正则

#1、BRE

基本正则表达式,grep、sed、vi等软件支持。vim有扩展。

2、ERE

扩展正则表达式,egrep(grep -E)、sed -r等。

3、PCRE

几乎所有高级语言都是PCRE的方言或变种。

Python从1.6使用SRE正则表达式引擎,可以认为是PCRE的子集,见模块re。

 

元字符:

.            匹配除换行符外任意一个字符

import re

s = 'very'
regex = re.compile('.')
matcher = regex.match(s)
print(matcher)
<_sre.SRE_Match object; span=(0, 1), match='v'>

[abc]         字符集合,只能表示一位字符位置,匹配所包含的任意一个字符

[^abc]       字符集合,只能表示一位字符位置,匹配除去集合内字符的任意一个字符

[a-z]          字符范围,也是个集合,表示一个字符位置,匹配所包含的任意一个字符   常用[A-Z],[0-9]

[^a-z]        字符范围,也是个集合,表示一个字符位置,匹配除去集合内字符的任意一个字符

\b              匹配单词的边界                \bb 在文件中找到单词中b开头的b字符   

例如 \bb.  # verybi beautiful 匹配be

\B              不匹配单词的边界           t\B包含t的单词但是不以t结尾的t字符  例如write

                  \Bb 不以b开头的含有b的单词,例如able

\d               [0-9]匹配1位数字

\D               [^0-9]匹配一位非数字

\s                匹配一位空白字符,包括换行符、制表符、空格 [\f\r\n\t\v]

\S               匹配一位非空白字符

\w               匹配[a-zA-Z0-9_],包括中文的字

\W              匹配\w之外的字符

 

单行模式:

.可以匹配所有字符,包括换行符

^表示整个字符串的开头,$整个字符串的结尾

 

多行模式:

.可以匹配除了换行符之外的字符

^表示行首,$行尾

注意:字符串中看不见的换行符,\r\n会影响e$的测试,e$只能匹配e\n

举例:

very very happy

harry key

y$ 单行匹配key的y,多行匹配happy和key的y。

 

转义:

凡是在正则表达式中有特殊意义的符号,如果想使用它的本意,请使用\转义,反斜杠自身用\\

 

重复:

*  表示前面的正则表达式会重复0次或多次   e[a-z]*单词中有e然后后面a-z一位字符,0次或多次

+  表示前面的正则表达式重复至少1次     e[a-z]+单词中有e然后后面a-z一位字符,至少有一个

? 表示前面的正则表达式会重复0次或1次     e[a-z]?单词中有e然后后面a-z一位字符,会重复0次或1次

{n}重复固定的n次              e[a-z]{2}单词中e后面只能有a-z一位字符,2次

{n,}重复至少n次                 e[a-z]{1,} = e[a-z]+         e[a-z]{0,} = e[a-z]*       e[a-z]{0,1} = e[a-z]?

 {n,m}重复n到m次              e[a-z]{1,10}单词中e后面至少1个,至多10个a-z一位字符

 

匹配手机号码:1\d{10}

匹配座机:^0\d{2,3}-\d{7,8}

 

x|y   匹配x或者y  wood took foot food       使用w|food 或者(w|f)ood

(pattern) 分组

\数字   匹配对应的分组         (very)\1匹配veryvery,但捕获的分组是very

 

零宽断言

(?=exp) 断言exp一定在匹配的右边出来,也就是说断言后面一定跟个exp

f(?=oo) f后面一定有oo出现

(?<=exp) 断言exp一定出现在匹配的左边      (?<=f)ood、(?<=t)ook 分别匹配ood前有f,ook前有t

负向零宽断言

(?!exp) 断言exp一定不会出现在右侧,也就是说断言后面一定不是exp    \d{3}(?!\d) 断言三位数字后面一定不是数字

(?<!exp) 断言exp一定不会出现在左侧,也就是说断言前面一定不是exp      (?<!f)ook

(?#comment) 注释      f(?=oo)(?#这个后断言不捕获)

断言不占分组号

 

贪婪与非贪婪

*?  匹配任意次,但尽量可能少重复

+? 匹配至少一次,但尽量可能少重复

??   匹配0次到1次,但尽量可能少重复

{n,}?  匹配至少n次,但尽量可能少重复

{n,m}?  匹配至少n次,至多m次,但尽量可能少重复

very very  使用v.*y   v.*?y

 

引擎选项:

IgnoreCase  匹配时忽略大小写    py: re.I  re.IGNORECASE

Singleline     单行模式:可以匹配所有字符,包括\n     py:re.S    re.DOTALL

Multiline        多行模式:^行首  $行尾          py:re.M    re.MULTILINE

 

练习匹配一个0~999之间的任意数字

1

23

886

一位数:\d

二位数:[1-9]?\d

三位数:^([1-9]\d\d?|\d)$       也可以这样:^[1-9]?\d\d?$

 

ip地址:(?:(25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)

也可以通过IP地址解析库处理:

import socket
nw = socket.inet_aton('255.255.255.255')
print(nw, socket.inet_ntoa(nw))
b'\xff\xff\xff\xff' 255.255.255.255

选出含有ftp的链接,且文件类型是gz或者xz的文件名

.*ftp.*\.(gz|xz)

改进

.*ftp.*/([^/]*\.(?:gz|xz))  #捕获文件名分组

终版

(?<=.*ftp.*/)[^/]*\.(?:gz|xz 蓝色的左边一定出现红色表达式

 

编译:

re.compile(pattern,flags=0) 

可以设定flags,编译模式,返回正则表达式对象regex

pattern就是正则表达式字符串,flags是选项,正则表达式都需要被编译,为了提高效率

编译结果会被保存,下次用同样的pattern的时候,就不要再编译了

import re

s = '0123abc'

regex = re.compile('\d')

matcher = re.match('\d', s)  #只从头开始找 一匹配到就不往后找了 并且还显示找到的位置
print(type(matcher))
print(1, matcher)

matcher = re.match('a', s)    #只从0开始,不是就不找了
print(2, matcher)

matcher = regex.match(s)      #与1一样
print(3, type(matcher))
print(4, matcher)

matcher = regex.match(s, 3)   #可以选择起始位置查找
print(5, type(matcher))
print(6, matcher)

regex1 = re.compile('[ab]')   #有时,并不能确定起始位置,search不从头找,找到第一个就返回
matcher = regex1.search(s)   
print(7, type(matcher))
print(8, matcher)

matcher = regex1.search(s, 5)   #也可以定义起始位置,减少搜索时间
print(9, type(matcher))
print(10, matcher)

matcher = re.fullmatch('\w+', s)  #全匹配才能出来
print(11, matcher)
regex2 = re.compile('[ab]')
matcher = regex2.match(s, 5, 6)   #这里就是匹配一位a或b  从5开始可以配置到b
print(12, matcher)
#
<class '_sre.SRE_Match'>
1 <_sre.SRE_Match object; span=(0, 1), match='0'>
2 None
3 <class '_sre.SRE_Match'>
4 <_sre.SRE_Match object; span=(0, 1), match='0'>
5 <class '_sre.SRE_Match'>
6 <_sre.SRE_Match object; span=(3, 4), match='3'>
7 <class '_sre.SRE_Match'>
8 <_sre.SRE_Match object; span=(4, 5), match='a'>
9 <class '_sre.SRE_Match'>
10 <_sre.SRE_Match object; span=(5, 6), match='b'>
11 <_sre.SRE_Match object; span=(0, 7), match='0123abc'>
12 <_sre.SRE_Match object; span=(5, 6), match='b'>

以上都建议先编译,再使用的方法

match、search、fullmatch

import re

s = '''bottle\nbag\nnbig\napple'''

for x in enumerate(s):
    if x[0] % 8 == 0:
        print()
    print(x, end=' ')


#  match方法
print('--match--')
result = re.match('b', s)
print(1, result)
result = re.match('a', s)
print(2, result)  # 没找到,返回None
result = re.match('^a', s, re.M)  # 依然从头开始找
print(3, result)
result = re.match('^a', s, re.S)  # 依然从头开始找
print(4, result)

# 先编译,用正则表达式对象
regex = re.compile('a')
result = regex.match(s)  # 依然从头开始找
print(5, result)
result = regex.match(s, 16)  # 从索引16开始找
print(6, result)

# search方法
print('--search--')
result = re.search('a', s)  # 从头搜索至到第一个配置
print(7, result)
regex = re.compile('b')
result = regex.search(s, 1)
print(8, result)
regex = re.compile('^b', re.M)
result = regex.search(s)  # 不管是不是多行,找到第一个就返回
print(9, result)   # bottle的b
regex = re.compile('b', re.M)
result = regex.search(s, 8)
print(10, result)  # big

# fullmatch方法
result = re.fullmatch('bag', s)
print(11, result)
regex = re.compile('bottle\nbag\nnbig\napple')
result = regex.fullmatch(s)
print(12, result)
(0, 'b') (1, 'o') (2, 't') (3, 't') (4, 'l') (5, 'e') (6, '\n') (7, 'b') 
(8, 'a') (9, 'g') (10, '\n') (11, 'n') (12, 'b') (13, 'i') (14, 'g') (15, '\n') 
(16, 'a') (17, 'p') (18, 'p') (19, 'l') (20, 'e') 
--match--
1 <_sre.SRE_Match object; span=(0, 1), match='b'>
2 None
3 None
4 None
5 None
6 <_sre.SRE_Match object; span=(16, 17), match='a'>
--search--
7 <_sre.SRE_Match object; span=(8, 9), match='a'>
8 <_sre.SRE_Match object; span=(7, 8), match='b'>
9 <_sre.SRE_Match object; span=(0, 1), match='b'>
10 <_sre.SRE_Match object; span=(12, 13), match='b'>
11 None
12 <_sre.SRE_Match object; span=(0, 21), match='bottle\nbag\nnbig\napple'>

全部匹配

re.findall、re.finditer

import re

s = '''bottle\nbag\nnbig\nable'''

regex = re.compile('[ab]')

print('--findall--')
result = re.findall('b', s)
print(1, result)

matcher = regex.findall(s)
print(1.5, type(matcher))
print(1.8, matcher)

result = re.findall('a', s)
print(2, result)
result = re.findall('^b\w+', s, re.M)   #3与3.5意义相同
print(3, result)
regex = re.compile('^b\w+', re.M)
result = regex.findall(s)
print(3.5, result)
result = re.findall('^a', s, re.S)
print(4, result)
result = re.findall('^b', s, re.S)
print(5, result)
result = regex.findall(s, 7, 10)
print(6, result)

s = '0123abc'
regex1 = re.compile('[abc20]+')  # + : 表示前面的正则表达式重复至少出现1次
matcher1 = regex1.findall(s)
print(matcher1)


regex = re.compile('\D')
matcher = regex.findall(s)
print('a', matcher)
print(type(matcher))

matcher2 = regex.finditer(s)
print('b', matcher2)
print(type(matcher2))
1 ['b', 'b', 'b', 'b']
1.5 <class 'list'>
1.8 ['b', 'b', 'a', 'b', 'a', 'b']
2 ['a', 'a']
3 ['bottle', 'bag', 'big']
3.5 ['bottle', 'bag', 'big']
4 []
5 ['b']
6 ['b', 'a']
['0', '2', 'abc']
a ['a', 'b', 'c']
<class 'list'>
b <callable_iterator object at 0x00000121F1ADA8D0>
<class 'callable_iterator'>
<_sre.SRE_Match object; span=(4, 5), match='a'>   
<_sre.SRE_Match object; span=(5, 6), match='b'>
<_sre.SRE_Match object; span=(6, 7), match='c'>   #多次match的结果  返回的是match对象

'r'是防止字符转义的 如果路径中出现'\t'的话 不加r的话\t就会被转义 而加了'r'之后'\t'就能保留原有的样子

在字符串赋值的时候 前面加'r'可以防止字符串在时候的时候不被转义 原理是在转义字符前加'\'

s=r'\tt'

print(s)

Output:

'\tt'

s='\tt'

print(s)

Output:

'        t'
import re

s = '''bottle\nbag\nbig\nable'''
regex = re.compile(r'\bb\w+')  
#这里要加上原始字符r/R,原因是:python默认会把\b解码给ascii码8(退格符)

matcher = regex.findall(s)
print(matcher)
['bottle', 'bag', 'big']

匹配替换

import re

s = '''bottle\nbag\nbig\napple'''

regex = re.compile('b\wg')
result = regex.sub('magedu', s)
print(1, result)

result = regex.sub('magedu', s, 1)  # 替换1次
print(2, result)

regex = re.compile('\s+')
result = regex.subn('\t', s)
print(3, result)
1 bottle
magedu
magedu
apple
2 bottle
magedu
big
apple
3 ('bottle\tbag\tbig\tapple', 3)

分割字符串

import re
s = '''01 bottle
02 bag
03      big123
100       able'''

for x in enumerate(s):
    if x[0] % 8 == 0:
        print()
    print(x, end=' ')
print('\n')

print(0, s.split())  #做不到
result = re.split('[\s\d]+', s)   #这样big1 变成big不是想到的  下面改进
print(1, result)

regex = re.compile('^[\s\d]+')  # 字符串首  只作用于第一行 下面再改进
result = regex.split(s)
print(2, result)

regex = re.compile('^[\s\d]+', re.M)  # 行首  用多行模式
result = regex.split(s)
print(3, result)

regex = re.compile('\s+|(?<!\w)\d+')  # 终版改进
result = regex.split(s)
print(4, result)

regex = re.compile('\s+\d+\s+')   # 在最前面先加个空   然后以(空+数字+空)来分割
result = regex.split(' '+s)       # 效率最高
print(5, result)
0 ['01', 'bottle', '02', 'bag', '03', 'big123', '100', 'able']
1 ['', 'bottle', 'bag', 'big', 'able']
2 ['', 'bottle\n02 bag\n03      big123\n100       able']
3 ['', 'bottle\n', 'bag\n', 'big123\n', 'able']
4 ['', '', 'bottle', '', '', 'bag', '', '', 'big123', '', '', 'able']
5 ['', 'bottle', 'bag', 'big123', 'able']

分组

编号从1开始


import re

s = '''bottle\nbag\nbig\nable'''

regex = re.compile(r'b(?P<test1>\w+)(?P<test2>e)')  # 还可以用命名分组
# regex = re.compile(r'b(\w+)(?:e)')  # 不捕获分组(e)
matchers = regex.finditer(s)
print(matchers)
for matcher in matchers:
    print(matcher)
    print(matcher.groupdict())
    print(matcher.groups())
    print(matcher.group(0))
    print(matcher.group(1))
    print(matcher.group(2))
    print('----------------')
<callable_iterator object at 0x000001D23026CF60>
<_sre.SRE_Match object; span=(0, 6), match='bottle'>
{'test1': 'ottl', 'test2': 'e'}
('ottl', 'e')
bottle
ottl
e
----------------
<_sre.SRE_Match object; span=(16, 19), match='ble'>
{'test1': 'l', 'test2': 'e'}
('l', 'e')
ble
l
e
----------------

要非的方式来限定  只要不是"<"或">"的出现任意次

 

匹配身份证 最后用的断言  后面一定不是X或x或数字

或者:后面一定是跟的空

单词统计:

import re
from collections import defaultdict

regex = re.compile(r'[^\w-]+')


def wordcount(path):
    d = defaultdict(lambda: 0)
    with open(path, encoding='utf8') as f:
        for line in f:
            for j in regex.split(line):
                if len(j) >= 1:
                    d[j.lower()] += 1
    return d


for j in sorted(wordcount(r'E:\1.txt').items(), key=lambda x: x[1], reverse=True):
        print(j)


#i = 0  打印出现频率最多的前十次

# for j in sorted(wordcount(r'E:\1.txt').items(), key=lambda x: x[1], reverse=True):
#     if i < 10:
#         print(j)
#     i += 1
('the', 27)
('of', 15)
('to', 7)
('he', 5)
('on', 5)
('and', 4)
('french', 3)
('in', 3)
('after', 3)
('as', 3)
。
。
。

日志分析(初版):

import datetime

logline = '''123.125.71.36 - - [06/Apr/2017:18:09:25 +0800] \
"GET /o2o/media.html?menu=3 HTTP/1.1" 200 8642 "-" \
"Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"'''


def convert_time(timestr):
    fmtstr = "%d/%b/%Y:%H:%M:%S %z"
    dt = datetime.datetime.strptime(timestr, fmtstr)
    print(555, type(dt))
    return dt


def convert_request(request: str):
    print(444, dict(zip(('method', 'url', 'protocol'), request.split())))

    # keys = ['a', 'b', 'c']                    #通过zip函数,再dict成一个字典 如:'method': 'GET'
    # values = [1, 2, 3]
    # dictionary = dict(zip(keys, values))
    # print(dictionary)
    #
    # """
    # 输出:
    # {'a': 1, 'c': 3, 'b': 2}
    # """
    return dict(zip(('method', 'url', 'protocol'), request.split()))


names = ['remote', '', '', 'datetime', 'request', 'status', 'size', '', 'useragent']
ops = [None, None, None, convert_time, convert_request, int, int, None, None]

tmp = ""
fields = []
flag = False

for word in logline.split():
    if not flag:
        if word.startswith('[') or word.startswith('"'):
            tmp = word[1:]
            if word.endswith(']') or word.endswith('"'):
                tmp = word.strip(']"')
                fields.append(tmp)
            else:
                flag = True
        else:
            fields.append(word)
        continue

    if flag:
        if word.endswith(']') or word.endswith('"'):
            tmp += " " + word[:-1]
            fields.append(tmp)

            tmp = ""
            flag = False
        else:
            tmp += " " + word
        continue

print(111, fields)

d = {}
for i, field in enumerate(fields):
    key = names[i]
    print(222, ops[i], field)
    if ops[i]:
        d[key] = ops[i](field)
    else:
        d[key] = field

print(333, d)
111 ['123.125.71.36', '-', '-', '06/Apr/2017:18:09:25 +0800', 'GET /o2o/media.html?menu=3 HTTP/1.1', '200', '8642', '-', 'Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)']
222 None 123.125.71.36
222 None -
222 None -
222 <function convert_time at 0x000001E463B95B70> 06/Apr/2017:18:09:25 +0800
555 <class 'datetime.datetime'>
222 <function convert_request at 0x000001E463B95EA0> GET /o2o/media.html?menu=3 HTTP/1.1
444 {'protocol': 'HTTP/1.1', 'url': '/o2o/media.html?menu=3', 'method': 'GET'}
222 <class 'int'> 200
222 <class 'int'> 8642
222 None -
222 None Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)
333 {'': '-', 'useragent': 'Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)', 'remote': '123.125.71.36', 'status': 200, 'size': 8642, 'datetime': datetime.datetime(2017, 4, 6, 18, 9, 25, tzinfo=datetime.timezone(datetime.timedelta(0, 28800))), 'request': {'protocol': 'HTTP/1.1', 'url': '/o2o/media.html?menu=3', 'method': 'GET'}}

改进版:

import re
import datetime

logline = '''123.125.71.36 - - [06/Apr/2017:18:09:25 +0800] \
"GET /o2o/media.html?menu=3 HTTP/1.1" 200 8642 "-" \
"Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"'''


def extract(line):
    # 0.0.0.0  255.255.255.255
    pattern = '''(?P<remote>[\d\.]{7,15}) - - \[(?P<datetime>[^\[\]]+)\] "(?P<request>[^"]+)" (?P<status>\d+) \
(?P<size>\d+) "([^"]+)" "(?P<useragent>[^"]+)"'''
    regex = re.compile(pattern)
    matcher = regex.match(line)
    return matcher.groupdict()

# print(222, extract(logline))


def convert_time(timestr):  # 可改为lambda
    fmtstr = "%d/%b/%Y:%H:%M:%S %z"
    dt = datetime.datetime.strptime(timestr, fmtstr)
    # print(555, type(dt))
    return dt


def convert_request(request: str):  # 可改为lambda
    # print(444, dict(zip(('method', 'url', 'protocol'), request.split())))
    return dict(zip(('method', 'url', 'protocol'), request.split()))


ops = {'datetime': convert_time,  # 替换为lambda
       'request': convert_request,  # 替换为lambda
       'status': int,
       'size': int
       }

print('----------------------------------------------------------------')


d = {k: ops.get(k, lambda x: x)(v) for k, v in extract(logline).items()}  # 字典表达式,此语作用等同下四句

# for k, v in extract(logline).items():
#     # if ops.get(k):
#         # d[k] = ops[k](v)
#     d[k] = ops.get(k, lambda x: x)(v)  # 此句作用等同上二句

for k, v in d.items():
    print(k, v)
----------------------------------------------------------------
datetime 2017-04-06 18:09:25+08:00
useragent Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)
request {'url': '/o2o/media.html?menu=3', 'protocol': 'HTTP/1.1', 'method': 'GET'}
remote 123.125.71.36
status 200
size 8642

再改进版:

import re
import datetime

logline = '''123.125.71.36 - - [06/Apr/2017:18:09:25 +0800] \
"GET /o2o/media.html?menu=3 HTTP/1.1" 200 8642 "-" \
"Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"'''


def extract(line):
    pattern = '''(?P<remote>[\d\.]{7,15}) - - \[(?P<datetime>[^\[\]]+)\] "(?P<request>[^"]+)" (?P<status>\d+) \
(?P<size>\d+) "([^"]+)" "(?P<useragent>[^"]+)"'''
    regex = re.compile(pattern)
    matcher = regex.match(line)
    return matcher.groupdict()


ops = {'datetime': lambda timestr: datetime.datetime.strptime(timestr, "%d/%b/%Y:%H:%M:%S %z"),
       'request': lambda request: dict(zip(('method', 'url', 'protocol'), request.split())),
       'status': int,
       'size': int
       }

d = {k: ops.get(k, lambda x: x)(v) for k, v in extract(logline).items()}

for k, v in d.items():
    print(k, v)
status 200
datetime 2017-04-06 18:09:25+08:00
useragent Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)
size 8642
remote 123.125.71.36
request {'method': 'GET', 'url': '/o2o/media.html?menu=3', 'protocol': 'HTTP/1.1'}

终版:

import re
import datetime

logline = '''123.125.71.36 - - [06/Apr/2017:18:09:25 +0800] \
"GET /o2o/media.html?menu=3 HTTP/1.1" 200 8642 "-" \
"Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"'''


pattern = '''(?P<remote>[\d\.]{7,15}) - - \[(?P<datetime>[^\[\]]+)\] "(?P<request>[^"]+)" (?P<status>\d+) \
(?P<size>\d+) "([^"]+)" "(?P<useragent>[^"]+)"'''
regex = re.compile(pattern)


def extract(line):
    matcher = regex.match(line)
    if matcher:
        return {k: ops.get(k, lambda x: x)(v) for k, v in matcher.groupdict().items()}


ops = {'datetime': lambda timestr: datetime.datetime.strptime(timestr, "%d/%b/%Y:%H:%M:%S %z"),
       'request': lambda request: dict(zip(('method', 'url', 'protocol'), request.split())),
       'status': int,
       'size': int
       }

lines = []
for l in lines:
    d = extract(l)
    if d:
        pass
    else:
        pass  # 看看不合格的有多少

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值