文本处理已经成为计算机常见工作之一, 对文本的搜索、定位、提取的逻辑往往比较复杂, 为了解决上述问题,产生正则表达式技术
正则表达式即文本的高级匹配模式,提供搜索,替代,获取等功能。本质是由一系列特殊符号和字符构成的自串,这个自串就是正则表达式。
正则表达式能够方便地进行检索和修改等文本操作;灵活多样;支持语言众多;
正则表达式匹配手段:通过设定有特殊意义的符号,描述符号和字符的重复行为及位置特征来表示一类特定规则的字符串
import re
re.findall(pattern, string)
参数:
返回值:
匹配规则:每个普通字符匹配其对应的字符
re.findall('ab',"abcdefabcd")
# ['ab', 'ab']
元字符: |
匹配规则:匹配 | 两侧的正则表达式
re.findall('com|cn',"www.baidu.com/www.cdsy.xyz")
# ['com', 'cn']
元字符: .
匹配规则:匹配除 \n外任意一个字符
re.findall('张.丰',"张三丰,张四丰,张五丰")
# ['张三丰', '张四丰', '张五丰']
元字符: [字符集]
匹配规则: 匹配字符集中的任意一个字符
表达形式:
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']
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次或多次
re.findall('[A-Z][a-z]+',"Hello World")
# ['Hello', 'World']
# 匹配整数
re.findall('-?[0-9]+',"Jame,age:18, -26")
# ['18', '-26']
# 匹配手机号码
re.findall('1[0-9]{10}',"Jame:13886495728")
# ['13886495728']
# 匹配qq号
re.findall('[1-9][0-9]{5,10}',"Baron:1259296994")
# ['1259296994']
# 匹配端口
re.findall('\d{1,5}',"Mysql: 3306, http:80")
# ['3306', '80']
re.findall('\w+',"server_port = 8888")
# ['server_port', '8888']
re.findall('\w+\s+\w+',"hello world")
# ['hello world']
re.findall("\Ahello","hello world")
# ['hello']
re.findall("world\Z","hello world")
# ['world']
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)
View Code
分组总结
捕获组
可以给正则表达式的子组起一个名字,表达该子组的意义。这种有名称的子组即为捕获组。
格式: (?P<name>pattern)
# 给子组命名为 "pig"
re.search(r'(?P<pig>ab)+',"ababababab").group('pig')
# 'ab'
注意事项
re.match(pattern, string, flags=0)
参数:
返回值:匹配内容match 对象
re.match尝试从字符串的起始位置匹配一个模型,如果不是起始位置匹配成功的话,match()就返回none.
import re
print(re.match('www', 'www.cdsy.xyz').span()) # 在起始位置匹配, (0, 3)
print(re.match('xyz', 'www.cdsy.xyz')) # 不在起始位置匹配, None
我们可以使用group()或groups()匹配对象函数来匹配表达式.
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(pattern, string, flags=0)
参数:
返回值: 匹配目标字符串第一个符合内容 的对象
我们可以使用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(pattern,replace,string,max,flags = 0)
使用一个字符串替换正则表达式匹配到的内容
参数:
返回值: 替换后的字符串
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)
regex = compile(pattern,flags = 0)
参数:
返回值: 正则表达式对象
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(pattern,string,flags = 0)
参数:
返回值: 在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
注意: 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)根据正则表达式匹配目标字符串内容
参数:
返回值: 匹配结果的迭代器, 返回的对象要通过.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)
参数:
返回值:完全匹配某个目标字符串 object
# 完全匹配
print(re.fullmatch("[,\w]+", s).group()) # 2019年,建国70年
re.split(pattern,string,flags = 0)使用正则表达式匹配内容,切割目标字符串
参数:
返回值: 切割后的内容列表
s = "Alex:1994,Sunny:1993"
# 按照匹配内容切割字符串
l = re.split(r'[:,]', s)
print(l) # ['Alex', '1994', 'Sunny', '1993']