1,匹配符号python
基本元字符正则表达式
空白字符编程
特定字符编程语言
重复匹配spa
位置匹配code
2,字符集对象
字符集[]的规范/元字符不一样于正则式主体blog
字符集中的特有元字符:字符串
正则表达式主体中的元字符,在字符集中无需转义的(转义了也能够,pycharm提示多余的):pycharm
匹配举例:
备注:若是想经过字符集[]匹配转义符\,则正则表达式引擎必须接受字符\\:
print('a[\]b') # a[\]b print('a[\\]b') # a[\]b,和单\结果同样,由于单\会自动转义,详见后面的转移章节说明 print(r'a[\\]b') # a[\\]b,OK!
3,匹配flags
re.M可使得^和$匹配'\n'后和'\n'前得位置,例如:
用'\n'也能够匹配结果,可是'\n'不是位置匹配,会把'\n'也匹配在内。
>>> s = 'aaa\nbbb\nccc' >>> r = re.search('^bbb$', s) >>> r # 匹配不到 >>> r = re.search('^bbb$', s, re.M) >>> r.group() 'bbb' >>> r = re.search('\nbbb\n', s, re.M) >>> r.group() '\nbbb\n'
总结:通过re.M改造后的^和$,至关因而向前查找和向后查找结合了‘\n’,例如:
s = 'start\nhello guxh2\nend' print(s) """ start hello guxh2 end """ print(repr(s)) 'start\nhello guxh2\nend' >>> re.findall(r'^hello .*?$', s, re.M) ['hello guxh2'] >>> re.findall(r'(?<=\n)hello .*?(?=\n)', s) ['hello guxh2'] >>> re.findall(r'\nhello .*?\n', s) # 不加定界会匹配上\n re.findall(r'\nhello .*?\n', s)
4,匹配模式
5,匹配写法
方法一,须要重复执行匹配的,先编译再匹配
好处是多个地方用到这个匹配模式regex,想修改时只须要修改一个地方
regex = re.compile(p)
regex.search(s)
re.search(regex, s)
方法二,简单的能够直接匹配
re.search(p, s)
6,python提供的匹配方法
1)re.search(p, s)
从s中提取彻底符合p的内容,只提取第一次命中的,返回re.match对象
提取总体匹配结果:searchObj.group(), searchObj.group(0)
提取全部分组捕获结果:searchObj.groups()
提取单个分组捕获结果:searchObj.group(n), 即groups()[i] = group(i+1)
2)re.findall(p, s)
提取全部符合p的内容,返回字符串组成的列表
使用分组捕获时,返回分组捕获结果(元组)组成的列表
re.search使用分组捕获时既能够捕获总体匹配,也能够捕获分组内容
re.findall使用分组捕获时只能捕获分组内容
3)re.match(p, s)
能够用re.search(^)替代
MULTILINE模式下,match也只匹配s,但re.search能够匹配换行后的开始
4)re.finditer(p, s)
re.findall的惰性版,返回iterator
5)re.split(p, s)
根据p分割字符串s
6)re.sub(p, repl, s, count=n)
p是匹配到的值,repl是替换后值,s是m目标字符串,count是替换多少次
re.subn(p, repl)能够返回替换次数
7,懒惰匹配和贪婪匹配
*和+都是贪婪比配,尽量多
能够结合?变成懒惰匹配
>>>re.search('a.*?b', 'abab').group() ab >>>re.search('a.*?b', 'abab').group() abab >>>re.search('192\.168\.1\..*$', '192.168.1.12').group() 192.168.1.12 >>>re.search('192\.168\.1\..*?', '192.168.1.12').group() # 没有结束符,懒惰不匹配 192.168.1. >>>re.search('192\.168\.1\..*', '192.168.1.12').group() # 没有结束符,贪婪匹配至最后 192.168.1.12
8,转义
1)python语言的转义
'\n'在python中表明换行,print输出时会作特殊处理:
s = 'x\nz' print(s) # x(换行)z print(repr(s)) # 'x\nz'
要想print输出'x\nz',须要对'\'进行转义:
s = 'x\\nz' # 或s = r'x\nz' print(s) # x\nz print(repr(s)) # 'x\\nz'
若是'\' + '字符'没有特殊含义,python语言会自动对\进行转义:
s = 'x\yz' print(s) # x\yz print(repr(s)) # 'x\\yz',发现多了个'\'
自动转义时,PEP8会提示invalid escape sequence '\y',看来PEP8要求'\'+'字符'没有特殊含义时,统一作好转义!
2)正则表达式的转义 - 非元字符
传递给正则引擎的字符串regex的内容,是print(regex)的结果,而不是print(repr(s))的结果
假设s = 'x\yz',print(s)的结果是'x\yz',print(repr(s))的结果是'x\\yz',传递给正则表达式的是'x\yz'
匹配时,也是匹配对象字符串s的print(s)的结果
和编程语言的转义同样,正则引擎也有本身的转义规则,当'\' + '字符'属于元字符时,能够进行特殊处理,例如\w匹配字符,数字,以及'_'
可是若是'\' + '字符'不是元字符,即没有特殊含义时,正则引擎不会像编程语言那样自动进行转义,而是要求必须接收转义后的'\',例如:
s = r'x\yz' regex = 'x\\yz' # 或者regex = 'x\yz',print(regex)都是'x\yz',传递给正则引擎的就是'x\yz' re.search(regex, s) # re.error: bad escape \y at position 1
这样的字符串传递给正则引擎才能获得正确处理:
regex = r'x\\yz' # print(regex)是'x\\yz',print(repr(regex))是'x\\\\yz' regex = 'x\\\yz' # 同上 regex = 'x\\\\yz' # 同上
为何'x\\\yz'和'x\\\\yz'结果同样呢?由于对于'\\\y',前2个'\\'转义为'\',后面的'\y',python语言自动转义成了'\y'(一样也会收到PEP8提示)
3)正则表达式的转义 - 元字符
假设要匹配的字符串为'a(换行)b',即:
s = 'a\nb'
传递给正则表达式的字符串regex,通过转义其print(regex)结果应当为'a\nb',因而匹配写法为:
regex = r'a\nb' # 或者regex = 'a\\nb' re.search(regex, s) # 能够匹配中s
可是发现未经转义的'a\nb'也能匹配中(换成制表符'\t'也能匹配):
regex = 'a\nb' re.search(regex, s) # 也能匹配中
这是由于给字符串传递'a(换行)b'时,字符串刚好会将其识别为'\n',交给正则引擎时就可以正确去匹配'a(换行)b'
可是若是python语言字符串和正则引擎对字符的解释不同时,转义或者不转义效果就会不一样,例如:
s = 'a*' regex1 = 'a\x2A' regex2 = 'a\\x2A' re.search(regex1, s) # 匹配结果为a re.search(regex2, s) # 匹配结果为a*
对于'\x2A'(即*),正则表达式解释为匹配前一个字符0次,1次或屡次。
若是和\n同样,则不论是'a\x2A'仍是'a\\x2A'都能匹配中'a*'
但实际上,不转义'\'就没法匹配'a*',那是由于不转义的'*'在正则表达式中是元字符,被赋予了其余含义。而'*'在python语言字符串中却没有特殊含义。
9,捕获组
1)捕获组对现有匹配方法对影响
捕获组对re.findall的影响:
s = 'abcabcabc' re.findall('abc', s) # ['abc', 'abc', 'abc'] re.findall('a(b)c', s) # ['b', 'b', 'c'] re.findall('(a(b)c)') # [('abc', 'b'), ('abc', 'b'), ('abc', 'b')]
捕获组对re.sub的影响:
s = 'abcabcabc' re.sub('abc', 'x', s) # xxx re.sub('a(b)c', 'x', s) # xxx re.sub('(a(b)c)', 'x', s) # xxx
备注:看上去对sub不起做用,但捕获组能够用来回溯引用
2)捕获组的回溯引用
s = '<h1> hello world <h1>' re.search(r'<(h1)>.*?<\1>', s) # 能够匹配s re.findall(r'<(h1)>.*?<\1>', s) # ['h1'] re.sub(re.sub(r'<(h1)>(.*?)<\1>', r'\1,\2', s)) # h1, hello world,re.sub会对匹配中的整个字符串进行替换
3)捕获组的命名
>>>ID = "310115199012128765" >>>p = "(?P<dictrict>[0-9]{6})(?P<birthday>[0-9]{8})" >>>re.search(p, ID).groupdict() {'dictrict': '310115', 'birthday': '19901212'}
10,先后查找
先后查找属于位置匹配
(?<=s):向后查找,s后面的位置
(?=s):向前查找,s前面的位置
>>>s = 'ab1c ab2c\nab3c' >>>re.findall('(a.*?)(?:\s|$)', s) # $不能放字符集[]里面 ['ab1c', 'ab2c', 'ab3c'] >>>re.findall('((?<=b).*?)(?:\s|$)', s) # b后面的位置开始 ['1c', '2c', '3c'] >>>re.findall('((?=b).*?)(?:\s|$)', s) # b前面的位置开始 ['b1c', 'b2c', 'b3c']
先后查找和findall:
即便正则表达式捕获的是位置,可是有了捕获组之后,findall会返回捕获组的内容,参考以前的re.findall('a(b)c', s)
s = '1234567890' re.findall(r'(?<=\d)(?=(\d\d\d)+$)') # ['890', '890', '890'],实际上正则表达式匹配的是三个位置,但有了捕获组之后findall的返回结果是捕获组内容 re.findall(r'(?<=\d)(?=(?:\d\d\d)+$)') # ['', '', ''],改成非捕获型后,findall返回的是三个位置:1|234|567|890
先后查找和sub:
sub是对匹配到的内容进行替换,捕获组不会对替换结果产生影响,捕获组在sub中只是用户回溯引用,参考9捕获组中的回溯引用
re.sub(r'(?<=\d)(?=(\d\d\d)+$)', '|', s) # 1|234|567|890,对匹配到的内容(位置)进行替换 re.sub(r'(?<=\d)(?=(?:\d\d\d)+$)', '|', s) # 1|234|567|890,对匹配到的内容
11,匹配举例
'ab1c ab2c\nab3c'匹配例子:
>>>s = 'ab1c ab2c\nab3c' >>>re.search('a.*?c a.*?c', s).group() # 总体匹配 ab1c ab2c >>>re.search('a(.*?)c a(.*?)c', s).groups() # 总体匹配 + 捕获分组 'b1 b2' >>>re.search('a(\w(\w))c a(\w(\w))c', s).groups() # 总体匹配 + 捕获分组 ('b1', '1', 'b2', '2') >>>re.findall('a.*?c', s) # 全局匹配 ['ab1c', 'ab2c', 'ab3c'] >>>re.findall('a(.*?)c', s) # 全局匹配 + 捕获分组:只能捕获分组内容 ['b1', 'b2', 'b3'] >>>re.findall('a(\w(\w))c', s) # 全局匹配 + 捕获分组嵌套 [('b1', '1'), ('b2', '2'), ('b3', '3')] >>>re.findall('a.*?c$', s) ['ab3c'] >>>re.findall('a.*?c$', s, flags=re.M) # re.M影响$ ['ab1c ab2c', 'ab3c'] >>>re.findall('^a.*?c', s) ['ab1c'] >>>re.findall('^a.*?c', s, flags=re.M) # re.M影响^ ['ab1c', 'ab3c'] >>>re.search('a.*c', s).group() 'ab1c ab2c' >>>re.search('a.*c', s, flags=re.DOTALL).group() # re.DOTALL影响. 'ab1c ab2c\nab3c' >>>re.split('\s', s) ['ab1c', 'ab2c', 'ab3c'] >>>re.split('(\s)', s) # re.split + 分组能够看到分隔符 ['ab1c', ' ', 'ab2c', '\n', 'ab3c']
'a b, c ; d\ e'匹配例子:
>>>s = 'a b, c ; d\ e' >>>s 'a b, c ; d\\ e' # \被自动转成\\ >>>re.split(r'\s*[\s,;\\]\s*', s) ['a', 'b', 'c', 'd', 'e'] >>>re.split(r'\s*(?:\s|,|;|\\)\s*', s) # []的等效写法 ['a', 'b', 'c', 'd', 'e'] >>>re.split(r'\s*(\s|,|;|\\)\s*', s) # 不用?:只抓到了单个分隔符,用它去对s分割会包含单个分隔符 ['a', ' ', 'b', ',', 'c', ';', 'd', '\\', 'e'] >>> re.split(r'(\s*(?:\s|,|;|\\)\s*)', s) # 包含完整的分隔符 ['a', ' ', 'b', ', ', 'c', ' ; ', 'd', '\\ ', 'e']