一、正字表达式简介
概念:正则表达式,又称规则表达式(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。
正则表达式是对字符串(包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”))操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。正则表达式是一种文本模式,模式描述在搜索文本时要匹配的一个或多个字符串。(百度百科)
说白了就是用来匹配字符的一个模式,满足该模式的表示匹配上了,就能够做查询、获取、替换等字符操作了。
(一)正则表达式元字符
通配符 | 含义 | 正则示例 | 匹配结果 |
---|---|---|---|
reg1 | reg2 | 匹配正则表达式reg1或reg2 | foo | bar | foo |
+ | 匹配1次或者多次前面出现的正则表达式 | [a-z]+ | aasx |
? | 匹配0次或者1次前面出现的正则表达式 | first? | first |
{N} | 匹配N次前面出现的正则表达式 | *.c{2} | first.c abc.c |
{M,N} | 匹配M~N次前面出现的正则表达式 | *.c{0,1} | one.c |
. | 匹配任何字符(\n除外) | a.a | abc |
^ | 匹配字符串起始部分 | ^a | ab… |
$ | 匹配字符串终止部分 | .txt$ | a.txt |
* | 匹配0次或者多次前面出现的正则表达式 | a* | aaaaa |
[…] | 匹配来自字符集的任意单个字符 | [abc] | b |
[^…] | 不匹配次字符集中任意单个字符 | [^0-9] | a |
(*|+|?|{})? | 匹配上面频繁出现符号的非贪婪版 | (*|+|?|{})? | ({}) |
(…) | 匹配封闭的正则表达式,然后另存为子组 | ([0-1][0-9])? | 12 |
\d | 匹配任何十进制数字 | \d.txt | 1.txt |
\w | 匹配任何字母数字字符 | \w{2}txt | 1.txt |
\s | 匹配任何空格字符 | a\sb | a b |
\b | 匹配任何单词边界 | The\bdog | The dog |
\N | 匹配已保存的子组 | ([0-9])\1 | 1 |
\. | "\"是转义字符,用在特殊字符前 | a.txt | a.txt |
等价匹配:
- ?,*,+,\d,\w 都是等价字符
- ?等价于匹配长度{0,1}
- *等价于匹配长度{0,}
- +等价于匹配长度{1,}
- \d等价于[0-9]
- \D等价于[^0-9]
- \w等价于[A-Za-z_0-9]
- \W等价于[^A-Za-z_0-9]
**贪婪模式:**正则表达式一般趋向于最大长度匹配,也就是所谓的贪婪匹配。如上面使用模式p匹配字符串str,结果就是匹配到abcaxc(ab.*c)
**非贪婪模式:**就是匹配到结果就好,就少的匹配字符。如上面使用模式p匹配字符串str,结果就是匹配到abc(ab.*c)。一般在某个模式后面跟上?表示非贪婪
(二)常用正则表达式
二、python的re模块
python的re模块就是用来用正则表达式的方式封装了几个使用的函数
- match(pattern, string, flags = 0)使用带有可选标记的正则表达式的模式来匹配字符串。如果匹配成功,返回匹配对象,否则返回None。 从字符串头部开始匹配
import re
content = 'The 123456 is my one phone number.'
print(len(content)) #字符串长度
result = re.match(r'^The\s\d+\s\w*', content) #使用match匹配, 第一个参数为正则表达式, 第二个为要匹配的字符串
print(result)
print(result.group()) #输出匹配内容
print(result.span()) #输出匹配内容的位置索引
输出:
34
<_sre.SRE_Match object; span=(0, 13), match='The 123456 is'>
The 123456 is
(0, 13)
- search(pattern, string ,flags = 0) 使用可选标记搜索字符串中第一次出现的正则表达式模式。如果匹配成功,则返回匹配对象,否则返回None,与match()方法不同, 不需要从头部开始匹配。
import re
content = 'Other The 123456 is my one phone number.'
result = re.search('The.*?(\d+).*?number.', content)
print(result.group())
输出:
The 123456 is my one phone number.
- findall(pattern, string[,flags] ) 查找字符串中所有(非重复)出现的正则表达式模式,并返回一个匹配列表。 match()和search()都是返回匹配到的第一个内容就结束匹配, findall()是返回所有符合匹配规则的内容。
示例1:
import re
html = '''
<div id="songs-list">
<h2 class="title">歌单</h2>
<p class="introduction">歌单列表</p>
<ul id="list" class="list-group">
<li data-view="2">一路上有你</li>
<li data-view="7">
<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
</li>
<li data-view="4" class="active">
<a href="/3.mp3" singer="齐秦">往事随风</a>
</li>
<li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
<li data-view="5"><a href="/5.mp3" singer="程慧玲">记事本</a></li>
<li data-veiw="5">
<a href="/6.mp3" singer="邓丽君">但愿人长久</a>
</li>
</ul>
</div>
'''
result = re.findall('<li.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>', html, re.S)
if result:
print(result)
for res in result:
print(res[0], res[1], res[2])
输出:
[('/2.mp3', '任贤齐', '沧海一声笑'), ('/3.mp3', '齐秦', '往事随风'), ('/4.mp3', 'beyond', '光辉岁月'), ('/5.mp3', '程慧玲', '记事本'), ('/6.mp3', '邓丽君', '但愿人长久')]
/2.mp3 任贤齐 沧海一声笑
/3.mp3 齐秦 往事随风
/4.mp3 beyond 光辉岁月
/5.mp3 程慧玲 记事本
/6.mp3 邓丽君 但愿人长久
示例2:
import re
string="abcdefg acbdgef abcdgfe cadbgfe"
#带括号与不带括号的区别
#不带括号
regex=re.compile("((\w+)\s+\w+)")
print(regex.findall(string))
#输出:[('abcdefg acbdgef', 'abcdefg'), ('abcdgfe cadbgfe', 'abcdgfe')]
regex1=re.compile("(\w+)\s+\w+")
print(regex1.findall(string))
#输出:['abcdefg', 'abcdgfe']
regex2=re.compile("\w+\s+\w+")
print(regex2.findall(string))
#输出:['abcdefg acbdgef', 'abcdgfe cadbgfe']
备注:
第一个 regex 中是带有2个括号的,我们可以看到其输出是一个list 中包含2个 tuple
第二个 regex 中带有1个括号,其输出的内容就是括号匹配到的内容,而不是整个表达式所匹配到的结果。
第三个 regex 中不带有括号,其输出的内容就是整个表达式所匹配到的内容。
结论:findall()返回的是括号所匹配到的结果(如regex1),多个括号就会返回多个括号分别匹配到的结果(如regex),如果没有括号就返回就返回整条语句所匹配到的结果(如regex2)。所以在提取数据的时候就需要注意这个坑。
实际上是由其并不是python特有的,这是 正则 所特有的 , 任何一门高级语言使用正则都满足这个特点:有括号时只能匹配到括号中的内容,没有括号【相当于在最外层增加了一个括号】。在正则里面 “()” 代表的是分组的意思,一个括号代表一个分组,你只能匹配到"()"中的内容
- finditer(pattern, string[,flags] ) 与findall()相同,但返回的是一个迭代器。对于每一次匹配,迭代器都能返回一个匹配对象
- split(pattern, string, max = 0) 根据正则表达式的模式分隔符,split函数将字符串分割为列表,返回匹配列表,分割最多操作max次
- sub()方法,去除匹配的字符
import re
content = '54abc59de335f7778888g'
content = re.sub('\d+', '', content)
print(content)
输出:
abcdefg
- compile(pattern, flags = 0) 匹配任何可选的标记来编译正则表达式的模式,然后返回一个正则表达式对象,可以用来简化代码
import re
content1 = '2016-1-1 12:01'
content2 = '2017-1-1 12:02'
content3 = '2018-1-1 12:03'
pattern = re.compile('\d{2}:\d{2}')
result1 = re.sub(pattern, '', content1)
result2 = re.sub(pattern, '', content2)
result3 = re.sub(pattern, '', content3)
print(result1, result2, result3)
输出:
2016-1-1 2017-1-1 2018-1-1
- group(num=0)返回整个匹配对象,或者编号为num的特定子组
import re
content = 'The 123456 is my one phone number.'
print(len(content)) #字符串长度
result = re.match(r'^The\s(\d+)\sis', content) #使用match匹配, 第一个参数为正则表达式, 第二个为要匹配的字符串
print(result)
print(result.group()) #输出匹配内容
print(result.group(1)) #输出第一个被()包裹的特定子组
print(result.span()) #输出匹配内容的位置索引
输出:
34
<_sre.SRE_Match object; span=(0, 13), match='The 123456 is'>
The 123456 is
123456
(0, 13)
- 修饰符re.S
import re
content = '''The 123456 is
one of my phone.
'''
result = re.match('^The.*?(\d+).*?phone.', content, re.S)
if result:
print(result.group(1))
else:
print('result = None')
result2 = re.match('^The.*?(\d+).*?phone.', content)
if result2:
print(result2.group(1))
else:
print('result2 = None')
输出:
123456
result2 = None
- re.I(re.IGNORECASE)表示使匹配时,忽略大小
- re.M(re.MULTILINE)多行匹配,影响 ^ 和 $的行为
- re.X(re.VERBOSE)这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。