一、不用正则表达式查找文本模式
文本模式是一种人为规定的结构,现在有一个模式:3个数字-3个数字-4个数字
使用isPhoneNumber()函数来判断字符串是否匹配该模式
def isPhoneNumber(number):
if len(number) != 12:
return False
for i in range(0,3):
if not number[i].isdecimal():
return False
if number[3] != "-":
return False
for i in range(4,7):
if not number[i].isdecimal():
return False
if number[7] != "-":
return False
for i in range(8,12):
if not number[i].isdecimal():
return False
return True
print(isPhoneNumber(""))
print(isPhoneNumber("112-564-7896"))
print(isPhoneNumber("112-7896-532"))
上述代码缺陷在于它仅能处理一种模式,新增一个模式后,就要增加新的代码
如果想在更长的字符串中寻找电话号码,就必须添加更多代码来寻找电话号码模式:
message = "Call me at 415-555-1011 tomorrow, 415-555-9865 is my office"
for i in range(len(message)):
chunk = message[i:i+12]
if isPhoneNumber(chunk):
print("Phone number %s is found"%chunk)
print("done")
总结上述代码发现,如果在一个长度为百万的字符串中匹配模式,那么效率是十分低下的
二、使用正则表达式查找文本模式
1、创建正则表达式对象
1)正则表达式的函数均在re模块中,导入即可:import re
2)创建Regex模式对象:phoneNumRegex = re.compile(r"\d\d\d-\d\d\d-\d\d\d\d")
\d在正则表达式中表示一位数字字符,即0~9的数字
2、匹配Regex对象
整体代码如下:
import re
phoneNumRegex = re.compile(r"\d{3}-\d{3}-\d{4}")
mo = phoneNumRegex.search("My number is 123-456-7890")
print("My phone number is found: " + mo.group())
首先,对象使用search方法查找传入的字符串,寻找正则表达式的所有匹配。如果未找到正则表达式模式,则返回None。如果找到了,则返回一个Match对象,使用Match对象中的group()方法,返回查找字符串中实际匹配的文本
3、正则表达式过程匹配回顾
- import re
- re.compile()函数创建Regex对象
- 使用search()方法 返回Match对象
- 使用group()方法返回实际匹配文本的字符串
三、用正则表达式匹配更多模式
1、利用()分组
现在想将\d\d\d-\d\d\d-\d\d\d\d分成两组即\d\d\d和\d\d\d-\d\d\d\d,做法是(\d\d\d)-(\d\d\d-\d\d\d\d)
上边提到使用group()方法进行匹配字符串返回 ,因此操作如下:
mo.group()---->"123-456-7890"
mo.group(0)---->"123-456-7890"
mo.group(1)---->"123"
mo.group(2)---->"456-7890"
mo.groups()---->("123","456-7890")
group中的数字表示第几个分组
前边使用compile()方法时曾使用r进行原始字符串的转换, 从而消除\d中\的转义作用。
在正则表达式中,也存在一些特殊字符:. ^ $ * + ? { } [ ] \ | ( )
如果模式中刚好出现上述特殊字符,则需要用\进行转义操作,例如:r"(\(\d\))"
2、用管道匹配多个分组
字符 | 表示管道,r"a|b"表示匹配"a" 或"b",即若先遇到a,则返回a;先遇到b,则返回b
batRegex = re.compile(r"Batman|computer")
mo = batRegex.search("Batman has a computer")
print(mo.group())
mo = batRegex.search("A computer was bought by Batman")
print(mo.group())
也可匹配多个模式中的一个,允许提取公共项方式,假设希望匹配Batman、Batmobile、Batcopter和Batbar中的任意一个,则代码如下
batRegex = re.compile(r"Bat(man|mobile|copter|bar)")
mo = batRegex.search("Batman is rich")
print(mo.group())
print(mo.group(1))
3、用?实现可选择匹配
字符?表明它前边的分组在这个模式中是可选的,即无论这段文本在不在,正则表达式都会认为匹配。利用前边电话例子,寻找包含区号或不包含区号的电话号码:
phoneNumRegex = re.compile(r"(\d{3}-)?\d{3}-\d{4}")
mo = phoneNumRegex.search("My number is 123-456-7890")
print("My phone number is found: " + mo.group())
mo = phoneNumRegex.search("My number is 456-7890")
print("My phone number is found: " + mo.group())
4、用*匹配0次或多次
*前的分组可以在文本中出现任意次(包括0次)
phoneNumRegex = re.compile(r"(\d{3}-)*\d{3}-\d{4}")
mo = phoneNumRegex.search("My number is 456-7890")
print("My phone number is found: " + mo.group())
mo = phoneNumRegex.search("My number is 123-456-7890")
print("My phone number is found: " + mo.group())
mo = phoneNumRegex.search("My number is 123-456-223-7890")
print("My phone number is found: " + mo.group())
5、用+匹配1次或多次
+前的分组必须“至少出现1次”
phoneNumRegex = re.compile(r"(\d{3}-)+\d{3}-\d{4}")
mo = phoneNumRegex.search("My number is 456-7890")
if mo is None:
print("My phone number is not found")
mo = phoneNumRegex.search("My number is 123-456-7890")
print("My phone number is found: " + mo.group())
mo = phoneNumRegex.search("My number is 123-456-223-7890")
print("My phone number is found: " + mo.group())
6、用{}匹配特定次数
\d{3}表示让\d重复3次,即\d\d\d
1)指定循环次数:将一个分组重复特定次数,就在该分组后跟上{数字}
2)指定范围:
(ha){1,3}将匹配"ha"、"haha"、"hahaha"
(ha){1,}表示不限定最大值
(ha){,5}表示匹配0~5次
四、贪心和非贪心匹配
Python的正则表达式默认是“贪心的”,在有二义性的情况下,会尽可能匹配最长的字符串,例如(ha){1,3}去查找"hahaha"时只会返回"hahaha"。
{}?表示“非贪心”版本,即会尽可能匹配最短的字符串
greedyRegex = re.compile(r"(Ha){3,5}")
mo1 = greedyRegex.search("HaHaHaHaHa")
print(mo1.group())
unGreedyRegex = re.compile(r"(Ha){3,5}?")
mo2 = unGreedyRegex.search("HaHaHaHaHa")
print(mo2.group())
五、findall()方法
search()方法只返回一个match对象,包含被查找字符串中的“第一次”匹配的文本
findall()方法将返回一组字符串列表, 包含被查找字符串中的“所有”匹配的文本
findall()方法返回结果总结:
- 没有分组,将返回一个匹配字符串的列表
- 有分组,返回一个字符串的元组的列表,每个分组对应一个字符串
1)没有分组:
phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
print(phoneNumRegex.findall("Cell: 415-856-7854 work:235-000-7459"))
2)有分组
phoneNumRegex = re.compile(r'(\d\d\d)-(\d\d\d)-(\d\d\d\d)')
print(phoneNumRegex.findall("Cell: 415-856-7854 work:235-000-7459"))
六、字符分类
\d | 0~9的任意数字 |
\D | 除0~9数字外的任何字符 |
\w | 任何字母、数字或下划线字符(匹配“单词”字符) |
\W | 除字母、数字和下划线以外的任何字符 |
\s | 空格、制表符或换行符(匹配“空白”字符) |
\S | 除空格、制表符或换行符外的任何字符 |
\d+\s\w+表示匹配的文本有一个或多个数字( \d+),然后是一个空白字符(\s),接下来是一个或多个字母/数字/下划线字符(\w+)
七、建立自己的字符分类
使用[]定义自己的字符分类,在[]内普通的正则表达式符号不会被解释,因此无需加\进行转义
在做方括号后加上一个^字符,即可得到“非字符类”,即不在这个字符类中的所有字符
phoneNumRegex = re.compile(r'[aeiouAEIOU]')
print(phoneNumRegex.findall("Bittersweet as life is, It's still wonderful, and It's fascinating even in tragedy"))
phoneNumRegex = re.compile(r'[^aeiouAEIOU]')
print(phoneNumRegex.findall("Bittersweet as life is, It's still wonderful, and It's fascinating even in tragedy"))
八、插入字符和美元符号
在正则表达式开始处使用^符号,表示匹配必须发生在被查找文本开始处
在正则表达式末尾使用$符号,表示该字符串必须以这个正则表达式的模式结束
同时使用^和$符号,表示整个字符串必须匹配该模式
如果字符串违背上述规定,则查找结果返回None
未完待续………