正则表达式

REregular expressionregexregexp

是文本处理极为重要的技术,用它可以对字符串按照某种规则进行检索、替换;

1970年,unix之父ken thompson将正则表达式引入到unix文本编辑器edgrep命令中,由此正则表达式普及开来;

1980年后,perl语言对henry spencer编写的库,扩展了很多新的特性;

1997年开始,philip hazel开发出了perl compatible regular expressions,它被phphttpd等工具采用;

RE应用极为广泛,shell中处理文本的命令,各种高级编程语言都支持RE

 

https://www.w3cschool.cn/regex_rmjc/

regester.NET软件;

 

分类:

BRE,基本正则表达式,grepsedvi等软件支持,vim有扩展;

ERE,扩展正则表达式,egrepgrep -Esed -r等;

PCRE,几乎所有高级语言都是PCRE的方言或变种,python1.6开始使用SRE正则表达式引擎,可以认为是PCRE的子集,见模块re

 

基本语法:

meta character,元字符:

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

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

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

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

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

\bboundary边界,匹配单词的边界,例:\bb在文本中找到单词中b开头的b字符;

\B,不匹配单词的边界,例:t\B,包含t的单词但不以t结尾的t字符;例:\Bb,不以b开头的含有b的单词,如able

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

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

\sseparator分隔符,匹配1位空白字符,包括换行符、制表符、空格;

\S,匹配1位非空白字符;

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

\W,匹配\w之外的字符;

 

单行模式:

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

^表示整个字符串的开头;

$整个字符串的结尾;

 

多行模式:

.可以匹配除换行符之外的字符,换行符就不算了;

^表示行首,整个字符串的开始,开始指的是\n之后紧接着的下一个字符;

$表示行尾,整个字符串的结尾,结尾指的是\n之前紧接着的字符;

注:

win的换行符为\r\n,在使用regester时要注意,\r?$

RE匹配unicode字符,空白分割的整体都按单词算,如abe电话18621761169

 

例:e\r$,匹配文本abe

例:

1.jpg

注:单行模式&多行模式;

In [4]: s = '''very very happy

   ...: harry key

   ...: :

   ...: '''

In [11]: str1=re.compile('y$',re.M)

In [12]: str1.findall(s)

Out[12]: ['y', 'y']

 

转义:

凡是在RE中有特殊意义的符号,如果想使用它的本意,用\,反斜杠自身用\\

\r\n还是转义后代表回车、换行;

 

重复:

*,表示前面的RE会重复0次或多次;e\w*,单词中有e,后面非空白字符;

+,表示前面的RE重复至少1次;e\w+,单词中有e,后面至少一个非空白字符;

?,表示前面的RE重复0次或1次;e\w?,单词中有e,后面至多有一个非空白字符;

{n},表示前面的RE重复n次;e\w{1},单词中有e,后面只能有一个非空白字符;

{n,},表示前面的RE重复至少n次;e\w{1,}等价于e\w+e\w{0,}等价于e\w*

{n,m},表示前面的RE重复nm次;e\w{0,1}等价于e\w?e'w{1,10}单词中有e,至少1个至多10个非空白字符;

 

x|y,匹配xy

 

捕获:

(pattern),使用小括号指定一个子表达式,也叫分组,捕获后会自动分配组号,组号从1开始,可以改变优先级;

\数字,匹配对应的分组;

(?:pattern),如果仅仅为了改变优先级,就不需要捕获分组;

(?<name>exp)(?'name'exp),命名分组,分组捕获,但可通过name访问分组,很好用,可与dict对应;python的命名分组语法特殊要加P,格式:(?P<name>exp)

 

零宽断言:

(?=exp),零宽度正预测先行断言,断言exp一定在匹配的右边出现,即断言后面一定跟个exp;例:f(?=oo)f后一定有oo出现;

(?<=exp),零宽度正回顾后发断言,断言exp一定在匹配的左边出现,即前面一定有个exp前缀;例:(?<=f)oodood前一定有f(?<=t)akeake前一定有t出现;

 

负向零宽断言:

(?!exp),零宽度负预测先行断言,断言exp一定不会出现在右侧,即断言后面一定不是exp

(?<!exp),零宽度负回顾后发断言,断言exp一定不能出现在左侧,即断言前面一定不能是exp;例:(?<!f)oooo前面不是f(?<!f)oo(?!k)oo前面不是f,后面不是k

 

注:

零宽断言中的exp为常值,不可以写正则;

 

注释:

(?#comment),如(?<!f)oo(?!d)(?#test)

 

注:

断言不捕获,也不匹配,不占分组;

分组和捕获是同一个意思,能用简单表达式,就不用复杂的表达式;

 

例:

2.jpg

3.jpg

 

例:

\ba\w+|\bb\w+ --> \b(a|b)\w+ -->\b(?:a|b)\w+

 

例:

4.jpg

5.jpg

6.jpg

 

 

 

贪婪与非贪婪:

默认是贪婪模式,即尽量多匹配更长的字符串;

非贪婪,在重复的符号后加上一个?,就尽量的少匹配了;

 

*?,匹配任意项,但尽可能少重复;

+?,匹配至少1次,但尽可能少重复;

??,匹配0次或1次,但尽可能少重复;

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

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

 

引擎选项:

IgnoreCase,匹配时忽略大小写,re.Ire.IGNORECASE;写代码时,找到的字串要转为小写或大写才能成为dictkey

Singleline,单行模式,可以匹配所有字符,包括\nre.Sre.DOTALL

Multiline,多行模式,^行首,$行尾,re.Mre.MULTILINE

IgnorePatternWhitespace,忽略表达式中的空白字符,如果要使用空白字符用转义,#可以用来作注释,re.Xre.VERBOSE,一般不用此项;

 

例:

7.jpg

8.jpg

9.jpg

10.jpg

11.jpg

f\w+?t,常用;

 

例:

12.jpg

 

注:

http状态码分析;

引用(盗链);

User-Agentrobotspider);

PVUV

 

 

 

pythonre

re.Mre.MULTILINE,多行模式;

re.Sre.DOTALL,单行模式;

re.Ire.IGNORECASE,忽略大小写;

re.Xre.VERBOSE,忽略表达式中的空白字符,不用此项;

|,位或,开启多种选项,如re.M | re.I00010010010010001 | 0 = 11 | 1 = 10 | 1 = 10 | 0 = 0,两个中有一个为1则为1

 

re中的方法:

编译:

re.compile(pattern,flags=0)-->regex正则表达式对象,

Compile a regular expression pattern, returning a pattern object.

为提高效率,正则表达式需要被编译,编译后的结果被保存,下次使用同样pattern时,就不需要再次编译;re的其它方法为了提高效率都调用了compile()编译方法;

pattern,即正则表达式字符串,

flags,编译模式,即re.Mre.Sre.Ire.X

返回正则表达式对象regex

 

match系列(match,search,fullmatch):

re.match(pattern,string,flags=0)-->match object,从字符串开头匹配,找到第1个立即返回,返回match对象;

regex.match(string[,pos[,endpos]])-->match object,可重设开始位置和结束位置,返回match对象;

re.search(pattern,string,flags=0)-->match object,从头搜索,直到第1个匹配,返回match对象;

regex.search(string[,pos[,endpos]])-->match object,可重设开始位置和结束位置,返回match对象;

re.fullmatch(pattern,string,flags=0)-->match object,整个字符串和正则表达式匹配;

regex.fullmatch(string[,pos[,endpos]])-->match object,整个字符串和正则表达式匹配;

 

find系列(findallfinditer):

re.findall(pattern,string,flags=0)-->list,对整个字符串,从左至右匹配,返回所有匹配项的列表;

regex.findall(string[,pos[,endpos]])-->list,对整个字符串,从左至右匹配,返回所有匹配项的列表;

re.finditer(pattern,string,flags=0)-->iterator-->match object,对整个字符串从左至右匹配,返回所有匹配项,返回迭代器,注意每次迭代返回的是match object

regex.finditer(string[,pos[,endpos]])-->iterator-->match object,对整个字符串从左至右匹配,返回所有匹配项,返回迭代器,注意每次迭代返回的是match object

 

匹配替换:

re.sub(pattern,repl,string,count=0,flags=0)

使用pattern对字符串string进行匹配,对匹配项使用repl替换,repl可以是stringbytesfunction

regex.sub(repl,string,count=0),使用pattern对字符串string进行匹配,对匹配项使用repl替换,repl可以是stringbytesfunction

re.subn(pattern,repl,string,count=0,flags=0)-->2-tuple,同sub,返回一个元组,(new_string,number_of_subs_made)

regex.subn(repl,string,count=0)-->2-tuple,同sub,返回一个元组,(new_string,number_of_subs_made)

 

分割字符串:

字符串的分割函数太难用,不能指定多个字符进行分割,str.split()

re.split(pattern,string,maxsplit=0,flags=0),正则的分割;

 

分组:

pattern加上小括号,捕获的数据被放到了group组中;

只有match object,才能用group相关的方法,groups()group()groupdict()

match(),search()返回match object

findall()返回字符串列表,不能用group相关的方法;

finditer(),返回一个个的match object

python中使用了分组,如有匹配的结果,会在match object中;

group(num),使用此方式返回对应分组,1-num是对应的分组,分组编号从1开始,0返回整个匹配的字符串;

group('name'),如果使用了命名分组,可用此方式取分组;

groups(),返回所有组,各分组组成tuple

groupdict(),返回所有命名分组;

 

例:

折行处理:

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

 

for x in enumerate(s):

    if x[0] % 8 == 0:

        print()

    print(x,end=' ')

print('\n')

 

例:

import re

 

s = '0123abc'

 

matcher = re.match('\d', s)   #查看源码可知,查找之前会事先编译,python中,不想给别人用的方法名或变量名,前面加_,如_compile();锚定索引0,若开始处没找到,则不再找,找不到返回None,找到返回match object

print(type(matcher))

print(matcher)

print(matcher.span())   #记录着从哪开始到哪结束

print(matcher.start())   #匹配的索引

print(matcher.end())

 

regex = re.compile('\d')

matcher = regex.match(s)

print(type(matcher))

print(matcher)

 

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

matcher = regex.match(s,4)   #指定开始位置

print(type(matcher))

print(matcher)

输出:

<class '_sre.SRE_Match'>

<_sre.SRE_Match object; span=(0, 1), match='0'>

(0, 1)

0

1

<class '_sre.SRE_Match'>

<_sre.SRE_Match object; span=(0, 1), match='0'>

<class '_sre.SRE_Match'>

<_sre.SRE_Match object; span=(4, 5), match='a'>

 

例:

import re

 

s = '0123abc'

 

matcher = re.search('[ab]',s)

print(type(matcher))

print(matcher)

 

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

matcher = regex.search(s)

print(type(matcher))

print(matcher)

 

matcher = re.fullmatch('\w+',s)

print(matcher)

 

regex = re.compile('\w+')

matcher = regex.fullmatch(s)

print(matcher)

matcher = regex.fullmatch(s,4,6)

print(matcher)

输出:

<class '_sre.SRE_Match'>

<_sre.SRE_Match object; span=(4, 5), match='a'>

<class '_sre.SRE_Match'>

<_sre.SRE_Match object; span=(4, 5), match='a'>

<_sre.SRE_Match object; span=(0, 7), match='0123abc'>

<_sre.SRE_Match object; span=(0, 7), match='0123abc'>

<_sre.SRE_Match object; span=(4, 6), match='ab'>

 

例:

import re

 

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

 

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

matcher = regex.findall(s)

print(matcher)

 

matcher = regex.finditer(s)

print(type(matcher))

print(matcher)

for i in matcher:

    print(i)

输出:

['b', 'b', 'a', 'b', 'a']

<class 'callable_iterator'>

<callable_iterator object at 0x7f63d4987b00>   #next(matcher)for loop

<_sre.SRE_Match object; span=(0, 1), match='b'>

<_sre.SRE_Match object; span=(7, 8), match='b'>

<_sre.SRE_Match object; span=(8, 9), match='a'>

<_sre.SRE_Match object; span=(11, 12), match='b'>

<_sre.SRE_Match object; span=(15, 16), match='a'>

 

例:

import re

 

s = '''os.path([path])'''

 

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

newstr = regex.sub(' ',s)

print(newstr)

 

newstr = regex.subn(' ',s,3)

print(newstr)

输出:

os path([path])

('os path([path])', 1)

 

例:

import re

 

s = ''' 01 bottle

02 bag

03      big1

100         able

'''

 

result = re.split('[\s\d]+',s)

print(result)

 

regex = re.compile('^[\s\d]+',re.M)

result = regex.split(s)

print(result)

 

regex = re.compile('\s+|(?<!\w)\d+')

result = regex.split(s)

print(result)

 

regex = re.compile('\s+\d+\s+')

result = regex.split(s)

print(result)

 

例:

import re

 

s = '''bottle\nbig\nbag1\nable'''

 

regex = re.compile('(b\wg)')

matcher = regex.search(s)

print(matcher.groups())

print(matcher.group(0))

 

regex = re.compile('(b\w+)(e)')

matchers = regex.finditer(s)

for matcher in matchers:

    print(matcher)

    print(matcher.groups())

 

regex = re.compile('b(\w+)(?P<tail>e)')

matchers = regex.finditer(s)

for matcher in matchers:

    print(matcher.groups())

    print(matcher.groupdict('tail'))

输出:

('big',)

big

<_sre.SRE_Match object; span=(0, 6), match='bottle'>

('bottl', 'e')

<_sre.SRE_Match object; span=(17, 20), match='ble'>

('bl', 'e')

('ottl', 'e')

{'tail': 'e'}

('l', 'e')

{'tail': 'e'}

 

 

 

 

 

 

习题:

1、手机号码;

13.jpg

14.jpg

 

2、座机;

15.jpg

 

3、匹配0-999之间的任意数字;

\d{1,3}

一位数,\d

一位数或二位数,^[1-9]?\d\r?$   #\r?仅在win下使用

^[1-9]?\d\d?\r?$

^(?:[1-9]\d\d?|\d)\r?$

 

4ipv4点分四段;

(?:\d{1,3}\.){3}\d{1,3}

(?:(?:\d{1,2}|[0-2]\d{2})\.){3}(?:\d{1,2}|[0-2]\d{2})   #解决超出200的问题,仍有256的问题

1

In [8]: import socket

In [9]: new = socket.inet_aton('192.168.05.001')   #有异常则非ipv4地址

In [10]: print(new,socket.inet_ntoa(new))

b'\xc0\xa8\x05\x01' 192.168.5.1

2

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

 

5、含有ftp://的链接,且文件类型是gzxz的文件名;

^ftp://.*/(.+?(?:.gz|.xz))\r?$   #.*贪婪,.+?非贪婪

(?<=ftp.*/)[^/]*\.(?:gz|xz)   #直接取到文件名,零宽断言中的exp表达式为常值,不能写正则

(?<=ftp)(?:(?:.*/)([^/]*\.(?:gz|xz)))

 

6、匹配邮箱地址;

(?:^\w[\w\.-]*)@(?:\w[\w\.-]*\.[a-zA-Z]+)

 

7

1)匹配html标记内的内容;

<a href="www.magedu.com" target='_blank'>马哥教育</a>

(?<=>)\w+(?=<)

2<a href="www.magedu.com" target='_blank'>马哥<br>教育</a>

<a[^<>]*>(.+)</a>

3)取href中的网址;

<a[^<>]*href="([^<>]+?)"

 

8、匹配url

(?:\w+)://[\S]+

(?:\w+)://[^\s]+

注:

url中不能有空格或空白,如有要用url编码处理;

 

9、匹配×××(15位,18位);

\d{17}[xX\d]|\d{15}

 

10、强密码,10-15位,包含大写字母、小写字母、数字、下划线,四种类型同时出现才是合格的强密码;

\w{10,15},弱密码;

判断先后:

\W

{10,15}

_   #_为最强密码

[A-Z]

\d

[a-z]   #最常用

 

11word count

16.jpg

17.jpg

18.jpg

注:

\b\w+\b

[^\.\(\)\[\]]+

 

19.jpg

 

import re

from collections import defaultdict

 

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

 

def wordcount(path):

    d = defaultdict(lambda: 0)

    with open(path) as f:

        for line in f:

            for sub in regex.split(line):

                if len(sub) >= 1:

                    d[sub.lower()] += 1

    return d

 

x = 0

for i in sorted(wordcount('/home/python/magedu/projects/cmdb/example.txt').items(),\

                key=lambda i:i[1],reverse=True):

    if x < 10:

        print(i)

    x += 1

输出:

('path', 166)

('if', 79)

('the', 74)

('a', 57)

('p', 57)

('return', 56)

('is', 52)

('and', 50)

('os', 47)

('in', 44)