day40:正则表达式

Posted 柯基家的咖啡猫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了day40:正则表达式相关的知识,希望对你有一定的参考价值。

正则表达式(regex)
定义:使用字符和特殊符号组成的能够描述某一类字符串的表达式。

动机:
    1.文本处理称为计算机的常见工作
    2.文本处理中,根据内容筛选、查找、匹配指定的内容又是常用功能之一
    3.方便快速的解决上述问题,正则表达式应运而生

特点和具体使用:
*是一种高级的文本搜素匹配模式,提供了丰富的功能
*正则表达式支持多种语言,使用方便
*MongoDB可以存储正则表达式
*在爬虫中大量使用正则表达式进行html文本的匹配
*Django,Tornado等框架中,路由的匹配

正则表达式规则及元字符
元字符:正则表达式中定义的,具有特定意义的符号

import re
re模块是python的标准库模块,是用来处理正则表达式的

re.findall(regex,string)
功能:使用regex去匹配string中的内容,如果匹配到则以一个列表的方式进行返回

*普通字符:
元字符:abc
匹配规则:匹配字符串的内容
示例:
In [6]: re.findall('abc123','abc123deabc123fgh')
Out[6]: ['abc123', 'abc123']

In [7]: re.findall('abc\.','abc.123deabc.123fgh')
Out[7]: ['abc.', 'abc.']

*或连接多个正则表达式
元字符:|
匹配规则:abc|def 表示既能匹配abc也能匹配def
示例:
In [8]: re.findall('abc|def','abcdksdefdfs')
Out[8]: ['abc', 'def']

In [9]: re.findall('abc|def|dfs','abcdksdefdfs')
Out[9]: ['abc', 'def', 'dfs']

In [11]: re.findall('abc|bcd','abcde’)#不能查找重叠
Out[11]: ['abc']

In [16]: re.findall('ab|cd','cdeabc')
Out[16]: ['cd', 'ab']
*竖线左右不要加空格,除非是要匹配空格
*不能查找重叠

*匹配任意一个字符
元字符:‘.’
匹配规则:使用“.”代表任意一个字符,不能代表‘\n’
例:f.o -> foo\fao\f@o\f5\…
In [12]: re.findall('f.o','from china')
Out[12]: ['fro']

In [13]: re.findall('f.o.','from china')
Out[13]: ['from']

*匹配字符串开头
元字符:^
匹配规则:匹配字符串的开头位置内容
例:^abc -> 以abc开头的字符串
In [17]: re.findall('^From','From China')
Out[17]: ['From']

In [18]: re.findall('^from','I come from China')
Out[18]: []

*匹配字符串结尾
元字符:$
匹配规则:当一个正则表达式是一个字符串结尾时能匹配出来
例:\.py$ -> test.py\find.py
In [21]: re.findall('py$','test.py find.py')
Out[21]: ['py']

In [22]: re.findall('py$','python')
Out[22]: []

In [23]: re.findall('.....py$','test.py find.py')
Out[23]: ['find.py']

*匹配0次或多次正则表达式
元字符:‘*’
匹配规则:用 * 匹配他前面出现的正则表达式0次或多次
例:
ab* ->  a\ab\abbb\abbbbb
a.* -> a\ab\ac\asgsfdg  都可以匹配到
In [24]: re.findall('ab*','abcde')
Out[24]: ['ab']

In [25]: re.findall('a.*','abcde') #.出现0次或多次
Out[25]: ['abcde']

In [26]: re.findall('ab*','abbbbbcde')
Out[26]: ['abbbbb']

In [27]: re.findall('ab*','abababab')
Out[27]: ['ab', 'ab', 'ab', 'ab']
*字符后面的*号代表多个前面的字符

*匹配前面的正则表达式一次或多次
元字符:‘+’
匹配规则:匹配前面出现的正则表达式至少出现一次
示例:
In [28]: re.findall('ab+','abbb')
Out[28]: ['abbb']

In [29]: re.findall('ab+','accbb')
Out[29]: []

*匹配前面出现的正则表达式0次或一次
元字符:’?’
匹配规则:匹配前面的正则表达式,0次,1次
示例:
In [32]: re.findall('ab?','abadf')
Out[32]: ['ab', 'a']

In [33]: re.findall('ab?','abbbb')
Out[33]: ['ab']

In [36]: re.findall('a?','abcbbb')
Out[36]: ['a', '', '', '', '', '', '']

*匹配前面的正则表达式指定的次数
元字符:{N} N表示一个正整数
匹配规则:匹配前面的正则表达式出现N次
示例:
In [37]: re.findall('a.{3}','absdfsaa')
Out[37]: ['absd']

In [38]: re.findall('ab{4}','abbbbbb')
Out[38]: ['abbbb']

*匹配前面出现的正则表达式指定次数区间
元字符:{m,n}
匹配规则:匹配前面的正则表达式m-n次
示例:
In [41]: re.findall('ab{3,6}','abbbcdbbbefgh')  
Out[41]: ['abbb']  #匹配最少3个,最多6个

In [42]: re.findall('ab{3,6}','abbbbbcdbbbefgh')
Out[42]: ['abbbbb']

In [43]: re.findall('.{6,8}','abcdefghi')
Out[43]: ['abcdefgh']

In [46]: re.findall('^.{6,8}','abcde’)#以任意的6-8位字符开头(可用作密码)
Out[46]: []  

*匹配字符集合
元字符:[abcdef] #里面可以传入任意字符,可以传入字符区间[a-z]、[A-Z]、[0-9]、[a-zA-Z0-9]
匹配规则:匹配集合中任意一个字符
例:
[abcd] -> a b c d
[a-zA-Z] -> 匹配所有字母 a A 。。。
[abc0-9] -> 匹配a b c 0 1 2 3 4 5 6 7 8 9
In [47]: re.findall('[0-9][abcd][A-Z]','1bDdg3cK')
Out[47]: ['1bD', '3cK']

In [48]: re.findall('^[0-9a-zA-Z]{6,8}$’,’asbd123kw')
Out[48]: []

In [118]: re.findall('^[0-9a-zA-Z]{6,8}$','asb123kw')
Out[118]: ['asb123kw']

*字符集合取非
元字符 [^…]
匹配规则:匹配任意一个非集合中的字符
例:
[^abcd] -> 可以匹配除了a b c d中的任意字符
[^_a-zA-Z0-9] -> 匹配任意一个特殊字符 $ ^ % * ( &
In [51]: re.findall('[^aeiou]','hello world')
Out[51]: ['h', 'l', 'l', ' ', 'w', 'r', 'l', 'd']

In [52]: re.findall('[^0-9]','hello 12306')
Out[52]: ['h', 'e', 'l', 'l', 'o', ' ']

*任意数字/非数字 字符
元字符:\d  \D
匹配规则:
    \d匹配任意一个数字字符 [0-9]
    \D匹配任意一个非数字字符 [^0-9]
例:
\d{3} -> 任意一个三位数
\D -> a % d $ * …(只要不是数字就行)
In [53]: re.findall('\d','hello 12306')
Out[53]: ['1', '2', '3', '0', '6']

In [54]: re.findall('\d{3}','hello 12306')
Out[54]: ['123']

In [55]: re.findall('\d{3}','hello 123068')
Out[55]: ['123', '068']

In [56]: re.findall('\D','hello 123068')
Out[56]: ['h', 'e', 'l', 'l', 'o', ' ']

In [57]: re.findall('\D{3}','hello 123068')
Out[57]: ['hel', 'lo ']

*匹配任意 数字字母下划线 或 非数字字母下划线
元字符:\w \W
匹配规则:
    \w匹配任意数字字母下划线 [_a-zA-Z0-9]
    \W匹配任意非数字字母下划线 [^_a-zA-Z0-9]
示例:
In [3]: re.findall('\w','hello')
Out[3]: ['h', 'e', 'l', 'l', 'o']

In [60]: re.findall('[A-Z]\w*','Hello World')
Out[60]: ['Hello', 'World']

In [62]: re.findall('\w*-\d+','xiaoming-64')
Out[62]: ['xiaoming-64']

In [63]: re.findall('\W','xiaoming-64')
Out[63]: ['-']

In [65]: re.findall('\w*','re_test.py')
Out[65]: ['re_test', '', 'py', '']

*匹配 空白字符 / 非空白字符
元字符:\s  \S
匹配规则:空白字符: ‘ ’、\n 、\r 、\0(字符串的结束标识) 、\t
    \s匹配其中任意一个空白字符
    \S匹配任意一个非空白字符
示例:
In [67]: re.findall('hello\s[a-zA-Z]*','hello China')
Out[67]: ['hello China']

In [68]: re.findall('hello\s[a-zA-Z]*','helloChina')
Out[68]: []

In [69]: re.findall('hello\s+China','hello   China')
Out[69]: ['hello   China']

In [70]: re.findall('\S*','hello China')
Out[70]: ['hello', '', 'China', '']   #空在讲函数的时候再议

*匹配字符串的开头和结尾(不常用)
元字符:\A  \Z
匹配规则:
    \A匹配字符串开头  ^
    \Z匹配字符串结尾  $
示例:
In [71]: re.findall('\Ahello','hello China')
Out[71]: ['hello']

In [72]: re.findall('\Ahello','aa hello China')
Out[72]: []

In [74]: re.findall('China\Z','hello China')
Out[74]: ['China']

In [76]: re.findall('\Ahello\Z','hello')  #绝对匹配
Out[76]: ['hello']

In [77]: re.findall('\Ahello\Z','hello China')
Out[77]: []

*匹配单词边界/非单词边界
元字符:\b  \B  (不代表任何字符,只是一个边界)
匹配规则:将连续字母认为是一个单词,而字母与非字母的交界认为是单词边界
示例:
In [79]: re.findall(r'\bBeijing\b','Welcome to Beijing')
Out[79]: ['Beijing']

In [80]: re.findall(r'\bjing\b','Welcome to Beijing')
Out[80]: []

In [82]: re.findall(r'jing\b','Welcome to Beijing')
Out[82]: ['jing']

In [85]: re.findall(r'\bto\b','Welcome to Beijing')
Out[85]: ['to']

In [86]: re.findall(r'\bto\b','Welcome to tornado')
Out[86]: ['to']  #只匹配出第一个

In [88]: re.findall(r'\bto\b','Welcome to to_rnado')
Out[88]: ['to']
*数字和下划线被认为可以是单词中的部分

总结:
字符:匹配实际字符
匹配单个字符:. [] \w \W \d \D \s \S
匹配正则表达式重复次数:* + ? {}
匹配位置(开头、结尾或边界):^ $ \A \Z \b \B
其他:| [^…]

raw(原始字符串格式)字符串特点:不会进行转义,将字符串内所有的内容原样使用
In [90]: re.findall('\\\\n','hello \\n')
Out[90]: ['\\n']

In [91]: re.findall(r'\\n','hello \\n')
Out[91]: ['\\n']

只要带\的前面都带上r
正则表达式转义
当正则表达式中要匹配 \ * . ? {} [] () ‘’ “” 这些字符时,需要使用’\’进行转义。此时如果为了避免字符串解析为正则表达式带来的麻烦,最好使用raw字符串
In [92]: re.findall('\"hello\"','he said "hello"')
Out[92]: ['"hello"']

In [93]: re.findall(r'"hello"','he said "hello"')
Out[93]: ['"hello"']

贪婪 & 非贪婪
贪婪模式:在默认情况下,正则表达式是贪婪模式,即使用 * + ? {m,n}的时候,都尽可能多的向后匹配内容
示例:
In [94]: re.findall(r'ab*','abbbbbbbbbbbbcd')
Out[94]: ['abbbbbbbbbbbb']

In [95]: re.findall(r'ab{3,5}','abbbbbbbbbbcd')
Out[95]: ['abbbbb']  #匹配3到5个,结果匹配5个

非贪婪模式:尽量少的匹配内容
贪婪 -> 非贪婪:贪婪后面加 ?
* -> *?
+ -> +?
? -> ??
{m,n} -> {m,n}?
示例:
In [96]: re.findall(r'ab*?','abbbbbbbbbbbbcd')
Out[96]: ['a']

In [97]: re.findall(r'ab{3,5}?','abbbbbbbbbbcd')
Out[97]: ['abbb']

正则表达式的分组:
*正则表达式可以分组,分组的标志即为(),每个括号中的内容,是整体正则表达式的一个子组,表达了一个小的整体       #只要加括号就是子组
*子组可以影响 * + ? {} 的重复行为,当重复时,把子组当作整体进行对待
示例:
In [100]: re.search('(ab)*','abbsdgfk').group()
Out[100]: 'ab'

In [101]: re.search('(ab)+','ababasdgfk').group()
Out[101]: 'abab'

*当有多个子组的时候,从外到内,从左到右,称为第一子组,第二子组,第三子组。。。

子组示例:
In [102]: re.search('(a(bc)de(fg)hij)','abcdefghijk').group() #默认是0
Out[102]: 'abcdefghij'

In [103]: re.search('(a(bc)de(fg)hij)','abcdefghijk').group(0)
Out[103]: 'abcdefghij'

In [104]: re.search('(a(bc)de(fg)hij)','abcdefghijk').group(1)
Out[104]: 'abcdefghij'

In [105]: re.search('(a(bc)de(fg)hij)','abcdefghijk').group(2)
Out[105]: 'bc'

In [106]: re.search('(a(bc)de(fg)hij)','abcdefghijk').group(3)
Out[106]: 'fg'

*可以给组起一个名字,称为捕获组           #将来在框架中可能会用到
格式:(?P<name>regex),其中name就是给组起的名字
调用格式:(?P=name)name是要调用的子组名称
例:
’hello (?P<cat>kitty)’  给kitty正则表达式的子组起了个名字cat

In [107]: re.search('hello (?P<cat>kitty)','hello kitty').group()
Out[107]: 'hello kitty'

In [110]: re.search('hello (?P<cat>kitty) (?P=cat)','hello kitty kitty').group()
Out[110]: 'hello kitty kitty'

练习:
1.匹配长度为8-10位的密码,必须以字母开头,密码可以是数字、字母、下划线组成
re.findall(‘^[a-zA-Z][_a-zA-Z0-9]{7,9}$’,’x123456789’)
^[a-zA-Z]\w{7,9}
2.匹配身份证号
re.findall(‘^[0-9]{17}X$’,’12345678901234567X’)
\d{17}(\d|X)
In [120]: re.search('\d{17}(\d|X)','370786199503141212').group()
Out[120]: '370786199503141212'
3.浮点数
^-?\d+\.\d+$
In [111]: re.search('^-?\d+\.\d+$','12.36').group()
Out[111]: '12.36'
4.整数或浮点数
^-?\d+(\.\d+)?$
In [116]: re.search('^-?\d+(\.\d+)?$','-12.8').group()
Out[116]: '-12.8'

re模块

compile()
功能:生成一个正则表达式对象
参数:传入一个正则表达式
返回值:返回正则表达式相应的对象

*正则表达式对象的一些属性函数 同 re模块可调用的一些函数名相同,用法相近
*这些函数多为常用的匹配显示函数
*使用的区别上只是用re直接调用的时候,第一个参数需要传入正则表达式,使用compile对象调用的时候则不用
示例:
In [121]: re.findall('\d{3}','123')
Out[121]: ['123']

In [124]: obj = re.compile('\d{3}')
In [125]: obj.findall('123')
Out[125]: ['123']

使用正则表达式对象obj调用时:(详见regex.py)
findall(string)
功能:获取字符串中能被正则表达式匹配的内容
参数:目标字符串
返回值:
    将匹配到的内容以列表形式返回
    当正则表达式有分组的时候,则每一项会显示每个分组匹配到的内容
示例:
import re
pattern = r'(hello) (\w+)'
# 获取正则表达式对象
obj = re.compile(pattern)
L = obj.findall('hello world hello ketty')
print('findall:', L)# [('hello', 'world'), ('hello', 'ketty')]

split()
功能:将一个字符串,按照正则表达式进行分割
参数:要分割的字符串
返回值:分割后的内容放入一个列表返回
示例:
L1 = re.split(r'\s+', 'hello  world nihao China')  
# ['hello', 'world', 'nihao', 'China']

sub(replacestr,string,count=0)
功能:使用replacestr的内容,替换string字符串中能被正则表达式匹配的部分
参数:
    replacestr:替换内容
    string:要匹配的字符串
    count:默认情况下替换所有匹配到的部分;如果赋值,表示最多替换count处
返回值:返回替换后的字符串
示例:
s = re.sub(r'[A-Z]', '###', 'Hello World Nihao', count=2)  
# sub: ###ello ###orld Nihao

subn()
同sub,只是返回值中多一个实际替换的个数
示例:
s1 = re.subn(r'[A-Z]', '###', 'Hello World Nihao', count=5)  
# sub: ('###ello ###orld ###ihao', 3)

match(string)
功能:匹配一个字符串,得到匹配结果
参数:要匹配的字符串
返回值:匹配到返回一个match object,没有匹配到返回None

*只有当正则表达式匹配的内容为字符串的开头的时候,才能匹配到,并且当有多处的时候,只能匹配一处
示例:
import re
# 获取正则表达式对象
re_obj = re.compile(r'foo')

match_obj = re_obj.match('foo is a fun')

print(match_obj)  # <_sre.SRE_Match object; span=(0, 3), match='foo'>
print(match_obj.group())  # foo

match_obj2 = re_obj.match('this is my food')

print(match_obj2)  #None
print(match_obj2.group()) #抛出异常

search()
同match
区别:可以匹配任意位置,且也是只匹配一处
示例:
re_obj = re.compile(r'foo')

search_obj = re_obj.search('this is my food')

print(search_obj)  # <_sre.SRE_Match object; span=(11, 14), match='foo'>
print(search_obj.group())  # foo

finditer()
同findall
区别:返回一个迭代对象,每个迭代对象都是一个match object
示例:
re_obj = re.compile(r'foo')

for i in re_obj.finditer('foo ! this is my food,foo'):
    print(i)
    print(i.group())  # 打印三次foo


match_obj的属性和函数:(详见regex2.py)
属性:
re:获取对应的正则表达式
pos:获取目标字符串的开始位置
endpos:获取目标字符串结尾位置
lastgroup:最后一组名称
lastindex:最后一组是第几组
示例:
import re

re_obj = re.compile('(ab)cd(?P<dog>ef)')

mobj = re_obj.search('hi,abcdefghij')

# mobj属性
print(mobj.re)  # 获取对应的正则表达式  re.compile('(ab)cd(?P<dog>ef)')
print(mobj.pos)  # 获取目标字符串开始位置 0
print(mobj.endpos)  # 获取目标字符串结尾位置 13

print(mobj.lastgroup)  # 最后一组名称 dog
print(mobj.lastindex)  # 最后一组是第几组 2

函数:
start():获取匹配到的字符串的开始位置
end():获取匹配到的字符串的结束位置
span():获取匹配到的字符串的起止位置
示例:
import re

re_obj = re.compile('(ab)cd(?P<dog>ef)')

mobj = re_obj.search('hi,abcdefghij')

# mobj属性函数
print(mobj.start())  # 获取匹配到的字符串的开始位置 3
print(mobj.end())  # 获取匹配到的字符串的结束位置 9
print(mobj.span())  # 获取匹配到的字符串的起止位置(3,9)

显示匹配结果:
group()
功能:显示匹配到的字符串
参数:
    如果不加,默认为0,表示返回整体的匹配结果;
    如果加一个数字,表示返回对应的组的匹配结果,如果越界会报错
返回值:返回相应的匹配结果
示例:
import re

re_obj = re.compile('(ab)cd(?P<dog>ef)')

mobj = re_obj.search('hi,abcdefghij')

print(mobj.group())  # abcdef
print(mobj.group(1))  # ab
print(mobj.group(2))  # ef
# print(mobj.group(3))  # 报错

groups()
功能:得到所有组匹配到的内容,以一个元组返回
参数:无
示例:
# 以元组的形式返回全部组的内容
print(mobj.groups())  # ('ab', 'ef')

groupdict()
功能:获取所有捕获组匹配内容,以字典返回(捕获组:起了名字的组),键为组名,值为匹配到的内容。
参数:无
示例:
# 返回所有捕获组内容
print(mobj.groupdict())  # {'dog': 'ef'}

以上是关于day40:正则表达式的主要内容,如果未能解决你的问题,请参考以下文章

day19——常用正则表达式re正则对象和正则匹配效率比较编译正则对象

重修课程day17(正则表达式)

day4 正则表达式(regular)

Day 11 正则表达式

python—day17 正则表达式 re模块

days24--正则表达式