正则表达式
文本查找实例
正则表达式主要是让使用者指定要查找的“模式”,而不是死板地查找固定的内容。
不用正则表达式查找内容
例如我们要查找长沙区号的电话号码,那么我们就需要知道特征,比如0731-84802110,0731是长沙的区号,紧接着就是-短横,然后是8位数字,那么我们就必须要符合这样的格式,否则我们将找到错误内容。
比如我们不能找到的是0732-84802110,也不能是073184802110,更不能是0731-长沙号码,这样就会查找错误。
这样我们就必须要一步一步来。
def isPhoneNumber(text): | |
if len(text) != 13: | |
return False | |
if text[4] != '-': | |
return False | |
if text[0] != '0': | |
return False | |
if text[1] != '7': | |
return False | |
if text[2] != '3': | |
return False | |
if text[3] != '1': | |
return False | |
for i in range(5, 13): | |
if not text[i].isdecimal(): | |
return False | |
return True | |
print(isPhoneNumber('text')) | |
print(isPhoneNumber('0731-84802110')) | |
print(isPhoneNumber('0731-8480211')) | |
print(isPhoneNumber('0732-84802110')) |
以上只是一个粗糙的代码,输出结果为:
False | |
True | |
False | |
False |
但是这是一种非常粗糙的查找方式,而且只能查找一种模式的电话号码,那么我们看看正则表达式。
正则表达式查找文本
基本方法流程
正则表达式,简称为regex,是文本模式的一种描述方法。
例如\d是一个正则表达式,表示一位数字字符,即任何一位0到9的数字,我们之前查找时可将文本视为0731-\d\d\d\d\d\d\d\d,这样其他字符串就不能匹配这样的内容了。
当然我们还可以用{}来表示匹配次数,如我们可将文本写为0731-\d{8}这样就不用打很多次\d了。
在使用正则表达式的之前,我们需要将python中的模块re导入:
import re |
在查找时,我们需要创建一个Regex对象来匹配电话号码:
import re | |
phoneNumRegex = re.compile('0731-\d{8}') | |
mo = phoneNumRegex.search('我的电话号码是0731-84802110') | |
print(mo.group()) |
注意我们始终操作的是字符串,所以不要漏引号。
以上代码非常简洁,它以这样的步骤运行:
利用正则表达式匹配更多模式
括号分组
我们可以用括号对查找文本进行分组,例如(\d\d\d\d)-(\d\d\d\d\d)前一组为1组,后一组为2组,当我们找到一个匹配的文本时,我们向group()方法中传递参数1,2,0或不传入时,我们依次会得到1组,2组与所有分组。
如果我们用groups()方法,我们将得到一个包含所有组的元组,就本例而言将得到(\d\d\d\d,\d\d\d\d\d).
如果我们需要对括号进行查询,我们就需要用转义字符\。
管道匹配
字符 | 被称为“管道”,它表示一种或的关系,我们如果利用这个字符查找,我们将有多种匹配,例如’batman | superman’我们将匹配batman或superman。
如果一句话中同时出现了两个内容,那么我们匹配到第一个出现的内容。
import re | |
phoneNumRegex = re.compile(r'0731 | 84802110') | |
mo1 = phoneNumRegex.search('我的电话号码是0731-84802110') | |
mo2 = phoneNumRegex.search('我的电话号码是84802110-0731') | |
mo1.group() | |
mo2.group() |
我们将输出一下内容:
0731 | |
84802110 |
我们可以复合使用,如(bat | super)man,这样我们也能同时匹配。
问号可选匹配
我们可以在字符串中使用?来匹配可选的内容,例如:如果我们想匹配man或者woman,那么我们可以写出(wo)?man,那么我们将能抓取到man与woman。
需要注意的是,两者同时出现的话,将优先按照文本顺序只挑出一个来。而且如果我们只查询woman的话,它也只会返回woman而不是man。
import re | |
phoneNumRegex = re.compile(r'(wo)?man') | |
mo = phoneNumRegex.search('woman and man') | |
print(mo.group(0)) | |
mo = phoneNumRegex.search('man and woman') | |
print(mo.group(0)) |
这样我们就实现的可选匹配。
*星号多次匹配
星号相较问号相比,相应内容可以多次出现,例如:(wo)*man,如果我们输入wowowowoman,我们也能匹配得到,当然如果没有wo,它照样能匹配。
+加号多次匹配
+与*相比,前面的分组必须出现至少一次,除此之外,它与*几乎没有区别。
{}花括号匹配特定次数
{min,max}可以指定特定匹配次数范围,这是一个闭区间,如果只输入一个数字且没有逗号,那么将会只匹配该次数。这个时候我们将会引出一个问题,如果我们匹配’哈哈哈’而正则表达式写为‘哈{1,3}’,那么它返回的是哈还是哈哈还是哈哈哈呢?
解决这个问题我们要知道贪心与非贪心匹配
贪心指的是python默认条件下将匹配尽可能长的字符串,此时上例我们将匹配到哈哈哈。
非贪心指的是匹配尽可能短的字符串。那么上例将匹配到哈。
由于python默认为贪心匹配,我们如果想使用非贪心匹配,需要在正则中 } 的末尾加上一个?以实现非贪心匹配。
findall()方法
findall()方法将返回一个包含所有匹配内容的列表。请注意,如果正则表达式里有分组,那么我们将会得到对应元组的列表。
字符分类
缩写字符分类 | 表示 |
---|---|
\d | 0到9的任何数字 |
\D | 除去0到9的数字以外的任何字符 |
\w | 任何字母、数字以及下划线的字符 |
\W | 除去\w匹配字符的任何字符 |
\s | 空格、制表符或换行符 |
\S | 除去\s匹配字符的任何字符 |
建立自己的字符分类
我们可以利用方括号来建立自己的字符分类,比如全部的元音字母[aeiouAEIOU],当我们用此建立一个Regex对象去查找文本的时候,我们将得到所有在文本中出现过的元音字母的列表。
方括号内不需要用转义字符转义来匹配特殊符号。
我们在字符分类的左方括号的后面加上 ^ 的时候,我们将匹配到除方括号内以外的所有字符,例如:[^aeiouAEIOU]
利用^和$来实现精准匹配
^
字符和 $
字符插入字符表示“必须以此开头”和“必须以此结尾”,例如’^hello’则表示文本必须以hello开头,如果两个符号合用,将实现完全精准匹配。
'^\d+$'这个正则表示从头至尾都是数字的字符串。
通配符
. 字符(英文句号)在正则中表示通配符,它匹配除了换行以外的所有字符。所以.at可以匹配mat,rat,cat等等。
如果用好搭配,就可以匹配任意字符,比如(.*)这个搭配可以匹配任意字符。
不区分大小写的匹配
我们在re.compile()方法中传入第二个参数即可实现大小写不区分的匹配。
robocop = re.compile(r'robocop',re.I) |
传入这个re.I之后我们就能实现该效果。
用sub()方法实现文本替换
namesRegex = re.compile('Agent \w+') | |
namesRegex.sub('Sucker','Agent Alice find the Agent Bob') |
我们将会得到以下输出:
Sucker find the Sucker |
由此我们得到文本替换的方法。
多行正则
如果正则表达式过长,它的可读性就将降低,此时我们需要依赖注释对其进行解释,从而提高可读性。
比如:
phoneRegex = re.compile(r'''( | |
(\d{3}| \(\d{3}\))? #地区号码 | |
(\s | - | \.)? #分隔符 | |
\d{3} #前三个号码 | |
(\s | - | \.)? #分隔符 | |
\d{4} #最后四个数字 | |
)''',re.VERBOSE) |
re.VERBOSE作为参数传入可以实现对空格和换行的忽略,这样我们就能实现多行正则表达式。
如果你还想忽略大小写,注意不能传入多个参数,我们需要在第二个参数后使用 | 的操作符来实现多功能复合。