正则表达式
引言
文本处理已经成为计算机常见工作之一, 对文本的搜索、定位、提取的逻辑往往比较复杂, 为了解决上述问题,产生正则表达式技术
正则表达式即文本的高级匹配模式,提供搜索,替代,获取等功能。本质是由一系列特殊符号和字符构成的自串,这个自串就是正则表达式。
正则表达式能够方便地进行检索和修改等文本操作;灵活多样;支持语言众多;
正则表达式匹配手段:通过设定有特殊意义的符号,描述符号和字符的重复行为及位置特征来表示一类特定规则的字符串
import re
re.findall(pattern, string)
参数:
- pattern: 以字符串的形式传入一个正则表达式
- string:要匹配的目标字符串
返回值:
- 得到一个列表,将目标子串中能用正则表达式匹配的内容放入列表
元字符的使用
普通字符
匹配规则:每个普通字符匹配其对应的字符
re.findall('ab',"abcdefabcd") # ['ab', 'ab']
或
元字符: |
匹配规则:匹配 | 两侧的正则表达式
re.findall('com|cn',"www.baidu.com/www.tmooc.cn") # ['com', 'cn']
匹配单个字符
元字符: .
匹配规则:匹配除 \n外任意一个字符
re.findall('张.丰',"张三丰,张四丰,张五丰") # ['张三丰', '张四丰', '张五丰']
匹配字符集
元字符: [字符集]
匹配规则: 匹配字符集中的任意一个字符
表达形式:
- [abc#!好] 表示 [ ] 中的任意一个字符
- [0-9], [a-z], [A-Z] 表示区间内的任意一个字符
- [_#?0-9a-z] 混合书写,一般区间表达写在后面
re.findall('[aeiou]',"How are you!") # ['o', 'a', 'e', 'o', 'u']
匹配字符集反集
元字符: [^字符集]
匹配规则: 匹配除了字符集以外的任意一个字符
re.findall('[^0-9]',"Use 007 port") # ['U', 's', 'e', ' ', ' ', 'p', 'o', 'r', 't']
匹配开始位置
- 元字符: ^
- 匹配规则: 匹配目标字符串的开头位置
re.findall('^Jame',"Jame,hello") # ['Jame']
匹配结束位置
- 元字符:$
- 匹配规则:匹配目标字符串的结束位置
re.findall('Jame$',"Hi,Jame") # ['Jame']
- 规则技巧: ^ 和 $必然出现在正则表达式的开头和结尾处。如果两者同时出现,则中间的部分必须匹配整个目标字符串的全部内容。
匹配字符重复
- 元字符: *
- 匹配规则: 匹配前面的字符出现0次或多次
re.findall("wo*", "woooo~~w!") # w后面o出现多次的情况和出现0次的情况 # ['woooo', 'w']
# 大写字母后面跟小写之母出现0次或多次 re.findall("[A-Z][a-z]*", "How ary you? Finf Ha") # ['How', 'Finf', 'Ha']
- 元字符: +
- 匹配规则: 匹配前面的字符出现后面的字符的次数1次或多次的情况
# 大写之母后面跟小写之母出现1次或多次 re.findall('[A-Z][a-z]+',"Hello World") # ['Hello', 'World']
- 元字符: ?
- 匹配规则: 匹配前面的字符出现0次或1次
# 匹配整数 re.findall('-?[0-9]+',"Jame,age:18, -26") # ['18', '-26']
- 元字符: {n}
- 匹配规则: 匹配前面的字符出现n次
# 匹配手机号码 re.findall('1[0-9]{10}',"Jame:13886495728") # ['13886495728']
- 元字符: {m,n}
- 匹配规则: 匹配前面的字符出现 [m-n] 次
# 匹配qq号 re.findall('[1-9][0-9]{5,10}',"Baron:1259296994") # ['1259296994']
匹配任意(非)数字字符
- 元字符: \d \D
- 匹配规则: \d 匹配任意数字字符, \D 匹配任意非数字字符
# 匹配端口 re.findall('\d{1,5}',"Mysql: 3306, http:80") # ['3306', '80']
匹配任意(非)普通字符
- 元字符: \w \W
- 匹配规则: \w 匹配普通字符, \W 匹配非普通字符
- 说明: 普通字符指 数字, 字母, 下划线, 汉字。
re.findall('\w+',"server_port = 8888") # ['server_port', '8888']
匹配任意(非)空字符
- 元字符: \s \S
- 匹配规则: \s 匹配空字符, \S 匹配非空字符
- 说明:空字符指 空格 \r \n \t \v \f 字符
re.findall('\w+\s+\w+',"hello world") # ['hello world']
匹配开头结尾位置
- 元字符: \A \Z
- 匹配规则: \A 表示开头位置, \Z 表示结尾位置
re.findall("\Ahello","hello world") # ['hello'] re.findall("world\Z","hello world") # ['world']
匹配(非)单词的边界位置
- 元字符: \b \B
- 匹配规则: \b 表示单词边界, \B 表示非单词边界
- 说明:单词边界指数字字母(汉字)下划线与其他字符的交界位置。
re.findall(r'\bis\b',"This is a test.") # ['is']
总结:
类别 | 元字符 |
匹配字符 | . [...] [^...] \d \D \w \W \s \S |
匹配重复 | * + ? {n} {m,n} |
匹配位置 | ^ $ \A \Z \b \B |
其他 | | () \ |
正则表达式的转义
1. 如果使用正则表达式匹配特殊字符则需要加 \ 表示转义。
特殊字符: . * + ? ^ $ [] () {} | \
# 匹配特殊字符 . 时使用 \. 表示本身含义 re.findall('-?\d+\.?\d*',"123,-123,1.23,-1.23") # ['123', '-123', '1.23', '-1.23']
2. 在编程语言中,常使用原生字符串书写正则表达式避免多重转义的麻烦。
python字符串 --> 正则 --> 目标字符串
"\\$\\d+" 解析为 \$\d+ 匹配 "$100"
"\\$\\d+" 等同于 r"\$\d+"
贪婪模式和非贪婪模式
贪婪模式: 默认情况下,匹配重复的元字符总是尽可能多的向后匹配内容。比如: * + ? {m,n}
非贪婪模式(懒惰模式): 让匹配重复的元字符尽可能少的向后匹配内容。
贪婪模式转换为非贪婪模式: 在匹配重复元字符后加 '?' 号即可
* ---> *? ; + ---> +? ; ? ---> ?? {m,n} ---> {m,n}?
re.findall("ab*", "abbbbb") # ['abbbbb'] re.findall("ab*?", "abbbbb") # ['a'] re.findall("ab+", "abbbbb") # ['abbbbb'] re.findall("ab+?", "abbbbb") # ['ab']
练习"
# 把数字匹配出来 re.findall("[^ ]+", "12 -36 28 1.34 -3.8") # ['12', '-36', '28', '1.34', '-3.8'] re.findall("-?\d+\.?\d*", "12 -36 28 1.34 -3.8") # ['12', '-36', '28', '1.34', '-3.8']
# 匹配一个.com邮箱格式字符串 print(re.findall(r"\w+@\w+", "lvze@163.com")) # 匹配一个密码 8-12位数字字母下划线构成 print(re.findall(r"\w{8,12}", "Tedu023256")) # 匹配一个数字 正数,负数,整数,小数,分数1/2,百分数45% print(re.findall(r"-?\d+/?\.?\d*%?", "12 -3 3.5 5.45 42% 1/3")) # 匹配一段文字中以大写字母开头的单词,注意文字中可能有ipython(不算)H-base(算),单词可能有大写字母小写之母 -_ print(re.findall(r"\b[A-Z][-_a-zA-Z]*", "Hello ipython H-base BSD"))
分组
在正则表达式中, 以 () 建立正则表达式的内部分组, 匹配括号内的表达式,分组的作用是在完整的模式中定义子模型,将每个圆括号中子模式专门匹配出来。
- 可以被作为整体操作, 改变元字符的操作对象
# 改变 +号 重复的对象 re.search(r'(ab)+',"ababababab").group() # 'ababababab' # 改变 |号 操作对象 re.search(r'(王|李)\w{1,3}',"王者荣耀").group() # '王者荣耀'
- 可以通过编程语言某些接口获取匹配内容中,子组对应的内容部分
# 获取url协议类型 re.search(r'(https|http|ftp|file)://\S+',"https://www.baidu.com").group(1) # 'https'
先匹配外部正则,再进一步匹配括号中的正则
import re s = 'A B C D' p1 = re.compile('\w+\s+\w+') print(p1.findall(s)) # # ['A B','C D'] p2 = re.compile('(\w+)\s+\w+') print(p2.findall(s)) # # ['A','C'] p3 = re.compile('(\w+)\s+(\w+)') print(p3.findall(s)) # # [('A','B'),('C','D')]
import re html = '''<div class="animal"> <p class="name"> <a title="Tiger"></a> </p> <p class="content"> Two tigers two tigers run fast </p> </div> <div class="animal"> <p class="name"> <a title="Rabbit"></a> </p> <p class="content"> Small white rabbit white and white </p> </div>''' pattern = re.compile( '<div class="animal">.*?title="(.*?)".*?' 'class="content">(.*?)</p>', re.S) r_list = pattern.findall(html) print(r_list)
分组总结
- 在网页中,想要什么内容,就加 ( )
- 先按整体正则匹配,然后再提取分组()中的内容
- 如果有2个及以上分组(),则结果中以元组形式显示 [(),(),()]
捕获组
可以给正则表达式的子组起一个名字,表达该子组的意义。这种有名称的子组即为捕获组。
格式: (?P<name>pattern)
# 给子组命名为 "pig" re.search(r'(?P<pig>ab)+',"ababababab").group('pig') # 'ab'
注意事项
- 一个正则表达式中可以包含多个子组
- 子组可以嵌套,但是不要重叠或者嵌套结构复杂
- 子组序列号一般从外到内,从左到右计数
Python re模块
re.match函数
re.match(pattern, string, flags=0)
参数:
- pattern 正则
- string 目标字符串
- flags 标志位, 用于控制表达式的匹配方式,如:是否区分大小写...
返回值:匹配内容match 对象
re.match尝试从字符串的起始位置匹配一个模型,如果不是起始位置匹配成功的话,match()就返回none.
import re print(re.match('www', 'www.runoob.com').span()) # 在起始位置匹配, (0, 3) print(re.match('com', 'www.runoob.com')) # 不在起始位置匹配, None
我们可以使用group()或groups()匹配对象函数来匹配表达式.
- group(num=0) 匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。
- groups() 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。
import re line = "Cats are smarter than dogs" # .* 表示任意匹配除换行符(\n、\r)之外的任何单个或多个字符 matchObj = re.match(r'(.*) are (.*?) .*', line) print(matchObj.group()) # Cats are smarter than dogs print(matchObj.group(1)) # Cats print(matchObj.group(2)) # smarter m = re.match(r"[A-Z]\w*", "Hello World") print(m.group()) # Hello
re.search函数
re.search 扫描整个字符串并返回第一个成功的匹配。
re.search(pattern, string, flags=0)
参数:
- pattern 正则
- string 目标字符串
- flags 标志位, 用于控制表达式的匹配方式,如:是否区分大小写...
返回值: 匹配目标字符串第一个符合内容 的对象
我们可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式。同re.match
import re line = "Cats are smarter than dogs" # .* 表示任意匹配除换行符(\n、\r)之外的任何单个或多个字符 searchObj = re.search(r'(.*) are (.*?) .*', line) print(searchObj) # <_sre.SRE_Match object; span=(0, 26), match='Cats are smarter than dogs'> print(searchObj.group()) # Cats are smarter than dogs print(searchObj.group(1)) # Cats print(searchObj.group(2)) # smarter # 匹配第一处 m = re.search(r"[A-Z]\w*", " Hello World") print(m.group()) # Hello
match和search区别: re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。
re.sub()替换函数
re.sub(pattern,replace,string,max,flags = 0)
使用一个字符串替换正则表达式匹配到的内容
参数:
- pattern 正则表达式
- replace 替换的字符串
- string 目标字符串
- max 最多替换几处, 默认替换全部
- flags 功能标志位,扩展正则表达式的匹配
返回值: 替换后的字符串
import re phone = "2004-959-559 # 这是一个电话号码" # 删除注释 num_1 = re.sub(r'#.*$', "", phone) # 2004-959-559 # 移除非数字的内容 num_2 = re.sub(r'\D', "", phone) # 2004959559 # 替换匹配到的内容 s = re.subn(r'\s+', '#', "This is a test", 2) # ('This#is#a test', 2)
compile 函数
regex = compile(pattern,flags = 0)
参数:
- pattern 正则表达式
- flags 功能标志位,扩展正则表达式的匹配
返回值: 正则表达式对象
compile 函数用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用。
import re pattern = re.compile(r'([a-z]+) ([a-z]+)', re.I) # re.I 表示忽略大小写 m = pattern.match('Hello World Wide Web') print(m) # 匹配成功,返回一个 Match 对象 # <_sre.SRE_Match object; span=(0, 11), match='Hello World'> print(m.group(0)) # 返回匹配成功的整个子串,Hello World print(m.group(1)) # 返回第一个分组匹配成功的子串,Hello print(m.groups()) # 等价于 (m.group(1), m.group(2), ...) ('Hello', 'World') print(m.group(3)) # 不存在第三个分组 报错!!! print(m.span(0)) # 返回匹配成功的整个子串的索引,(0, 11)
re.findall()函数
re.findall(pattern,string,flags = 0)
参数:
- pattern 正则表达式
- string 目标字符串
- flags 功能标志位,扩展正则表达式的匹配
返回值: 在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
注意: match 和 search 是匹配一次 findall 匹配所有。
import re # 目标字符串 s = "Alex:1994,Sunny:1993" pattern = r"(\w+):(\d+)" # re模块调用findall l = re.findall(pattern, s) print(l) # [('Alex', '1994'), ('Sunny', '1993')]
re.finditer(pattern,string,flags = 0) 根据正则表达式匹配目标字符串内容
参数:
- pattern 正则表达式
- string 目标字符串
- flags 功能标志位,扩展正则表达式的匹配
返回值: 匹配结果的迭代器, 返回的对象要通过.group()取值
import re s = "2019年,建国70年" pattern = r'\d+' # 返回迭代器 it = re.finditer(pattern,s) for i in it: print(i) # 获取match对象对应内容 # <_sre.SRE_Match object; span=(0, 4), match='2019'> # <_sre.SRE_Match object; span=(0, 4), match='70'>
re.fullmatch(pattern,string,flags=0)
参数:
- pattern 正则
- string 目标字符串
返回值:完全匹配某个目标字符串 object
# 完全匹配 print(re.fullmatch("[,\w]+", s).group()) # 2019年,建国70年
re.split(pattern,string,flags = 0) 使用正则表达式匹配内容,切割目标字符串
参数:
- pattern 正则表达式
- string 目标字符串
- flags 功能标志位,扩展正则表达式的匹配
返回值: 切割后的内容列表
s = "Alex:1994,Sunny:1993" # 按照匹配内容切割字符串 l = re.split(r'[:,]', s) print(l) # ['Alex', '1994', 'Sunny', '1993']