Python碎碎念(2):正则表达式
转载请在文章起始处注明出处,谢谢。
文章转自本人之前博客
0. 写在前面
正则表达式,是一种强大的匹配模式。
1. 使用正则表达式的步骤
a.用import re导入正则表达式模块;
b.用re.compile()函数创建一个Regex对象(记得使用原始字符串r);
c.向Regex对象的search()方法传入想查找的字符串,它返回一个Match()对象;
d.调用Match()对象的group()方法,返回实际匹配文本的字符串。
例如:
import re
phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
mo = phoneNumRegex.search('My number is 123-456-7890.') #mo是一个通用名称,用于Match对象。
print('Phone number found:' + mo.group())
注意:除了search()方法外,还有findall()方法。search()返回一个Match()对象,而findall()返回一组字符串列表,包含被查找字符串中所有匹配。若正则表达式中有分组,则返回一个元组的列表。
2. 正则表达式匹配更多模式
2.1利用括号进行分组
添加括号可以在正则表达式中创建“分组”,使用group()匹配对象的方法,从分组中获得匹配文本。
例如:
(\d\d\d)-(\d\d\d-\d\d\d\d) 如:123-456-7890
group(1) ----> '123'
group(2) ----> '456-7890'
group(0)或group() ----> '123-456-7890'
groups() ----> ('123','456-7890') #可以使用多重赋值的方法
2.2用管道匹配多个分组
字符’|’被称为管道,用来匹配许多表达式中的一个。
2.3问号实现可选匹配
‘?’匹配之前分组零次或一次。
2.4星号匹配零次或多次
‘*’匹配之前的分组零次或多次。
2.5加号匹配一次或多次
‘+’匹配之前的分组一次或多次
2.6花括号匹配特定的次数
例如:
(ha){3} ----> 'hahaha'
(ha){3,5} ----> 'hahaha'、'hahahaha'、'hahahahaha'
(ha){3,5}相当于管道匹配((hahaha)|(hahahaha)|(hahahahaha))
3. 贪心和非贪心匹配
Python正则表达式默认“贪心”匹配,即匹配最长的字符串。
花括号的“非贪心”匹配,匹配最短的字符串,要在结束的花括号后跟一个问号。
例如:
(ha){3,5} #贪心匹配
(ha){3,5}? #非贪心匹配
4. 字符分类
\d 0~9的任意数字
\D 除0~9的数字以外任何字符
\w 任何字母、数字、下划线字符(可以认为是匹配“单词”字符)
\W 除字母、数字、下划线字符以外的任何字符
\s 空格、制表符、换行符(可以认为是匹配“空白”字符)
\S 除空格、制表符、换行符以外的任何字符
例如:
[0-5] ----> 0~5
\d+\s\w+ ----> 12 words
5. 建立自己的字符分类
使用方括号[]定义自己的字符分类。
例如:
[aeiouAEIOU]匹配所有元音字符,不论大小。
注意:方括号内的普通正则表达式符号不会被解释,也就是说不需要加\。
在字符分类的左括号后加^,表示非字符类。
例如:
[^aeiouAEIOU]表示匹配所有的非元音字符。
6. 插入字符(^)和美元字符($)
在正则表达式开始处使用插入字符(^),表明匹配必须发生在被查找文本开始处。
在正则表达式末尾处加上美元字符($),表明字符串必须以这个正则表达式的模式结束。
例如:
正则表达式r'^\d+$'表明开始和末尾都是数字。
7. 通配字符
正则表达式中句点(.)被称为通配字符,匹配除换行之外的所有单个字符。
注意:句点字符只匹配一个字符。
7.1 点星匹配所有字符
.* 表示任意文本,使用的是贪心匹配,若想使用非贪心匹配,则需要在后面加?,即.*?。
7.2 句点字符匹配换行
.* 匹配所有除换行外的字符,通过传入re.DOTALL作为re.compile()的第二个参数,可以让句点字符匹配所有字符,包括换行符。
8. 不区分大小写的匹配
要让正则表达式不区分大小写,可以向re.compile()中传入re.IGNORECASE或re.I,作为第二个参数。
9. 用sub()方法替换字符串
Rxgex对象的sub()方法,传入两个参数,第一个参数是一个字符串,用来取代发现的匹配;第二个参数是正则表达式。sub()方法返回替换完成的字符串。
例如:
namesRegex = re.compile(r'Agent \w+')
namesRegex.sub('CENSORED','Agent Alice gave the secret documents to Aengt Bob.')
有时候,可能需要使用匹配文本本身,作为替换的一部分,在sub()的第一个参数中输入\1\2\3..,表示在替换中输入分组1、2、3..的文本。
例如:
import re
agentnameRegex = re.compile(r'Agent (\w)\w*')
print(agentnameRegex.sub(r'\1@@','Agent a told Agent b that Agent c knew Agent d was a double agent.'))
输出:
a@@ told b@@ that/ c@@ knew d@@ was a double agent.
10. 管理复杂的正则表达式
若正则表达式比较长、复杂,想忽略正则表达式中的空白符和注释,可以使用re.VERBOSE,作为第二个参数。
例如:
import re
phoneRegex = re.compile(r'''(
(\d{3}|\(\d{3}\))? #area code
(\s|-|\.)? #separator
\d{3} #first 3 digits
(\s|-|\.) #separator
\d{4} #last 4 digits
(\s*(ext|x|ext.)\s*\d{2,5})? #extension
)''', re.VERBOSE)
print(phoneRegex.findall('(123) 456-7890 ext. 123 123-456-7890'))
输出:
[('(123) 456-7890 ext. 123', '(123)', ' ', '-', ' ext. 123', 'ext.'), ('123-456-7890', '123', '-', '-', '', '')]
11. 项目:电话号码和Email地址提取程序
假设有一个无聊的任务,需要在一篇长的网页或者文章中,找出所有电话号码和邮件的地址。如果手动翻页,可能需要查找很长时间,如果有一个程序,可以在剪贴板的文本中查找电话号码和Email地址,那就只需要按一下Ctrl-A选择所有文本,按下Ctrl-C将它复制到剪贴板,然后运行程序。就可以自动找到电话号码和Email地址,替换掉剪贴板中的文本。
import re,pyperclip
#为电话号码创建一个正则表达式
phoneRegex = re.compile(r'''(
(\d{3,4}|\(\d{3,4}\))? #area code or telphone number
(\s|-|\.)? #separator
(\d{8}) #8 digits
(\s*(ext|x|ext.)\s*\d{2,5})? #extension
)''', re.VERBOSE)
#为Email创建一个正则表达式
emailRegex = re.compile(r'''(
[a-zA-Z0-9._%+-]+ #username
@
[a-zA-Z0-9.-]+ #domain name
(\.[a-zA-Z]{2,4}) #dot-something
)''',re.VERBOSE)
#在剪贴板文本中找到所有匹配
text = str(pyperclip.paste())
matches = []
for groups in phoneRegex.findall(text):
phoneNum = ' '.join([groups[1],groups[3]])
if groups[4] != '':
phoneNum += '-' + groups[4]
matches.append(phoneNum)
for groups in emailRegex.findall(text):
matches.append(groups[0])
#将所有匹配连接成一个字符串,复制到剪贴板
if len(matches) > 0:
pyperclip.copy('\n'.join(matches))
print('Copied to clipboard:')
print('\n'.join(matches))
else:
print('No phone numbers or email address found.')