记录博客 ZH-BLOG

Python 正则表达式(一)

时间:2018-07-19 16:53:11分类:python 基础

1.不用正则表达式情况下在一段文本中查找电话号码

def isPhoneNumber(text):
	if len(text)!=12:
		return False
	for i in range(0,3):
		if not text[i].isdecimal():
			return False
	if text[3]!='-':
		return False
	for i in range(4,7):
		if not text[i].isdecimal():
			return False
	if text[7]!='-':
		return False
	for i in range(8,12):
		if not text[i].isdecimal():
			return False
	return True

##text='415-555-4242'
##print(isPhoneNumber(text))

message='Call me at 415-555-1011 tomorrow. 415-555-9999 is my office.'
for i in range(0,len(message)-12+1):
	chunk=message[i:i+12]
	if isPhoneNumber(chunk):
		print('找到号码:'+chunk)

用正则表达式查找文本模式:正则表达式 \d\d\d-\d\d\d-\d\d\d\d, 来匹配前面 isPhoneNumber()函数匹配的同样文本: 3 个数字、一个短横线、 3 个数字、一个短横线、 4 个数字。

>>> import re
>>> phoneNunRegex=re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
>>> phoneNunRegex.search('My number is 415-555-4242.').group()
'415-555-4242'

注:search()会返回一个 Match 对象。 如果没有匹配值则是空 None,此时不能 group().否则AttributeError: 'NoneType' object has no attribute 'group'。

利用括号分组:正则表达式中创建“分组”:(\d\d\d)-(\d\d\d-\d\d\d\d)。第一对括号是第 1 组。第二对括号是第 2 组。向 group() 匹配对象方法传入整数 1 或 2, 就可以取得匹配文本的不同部分。 向 group()方法传入 0 或不传入参数, 将返回整个匹配的文本。

>>> import re
>>> phoneNumRegex=re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)')
>>> mo=phoneNumRegex.search('My number is 415-555-4242.')
>>> mo.group(0)
'415-555-4242'
>>> mo.group(1)
'415'
>>> mo.group(2)
'555-4242'

## 如果文本中有括号,需要用 \ 进行字符转义
>>> phoneNumRegex=re.compile(r'(\(\d\d\d\))-(\d\d\d-\d\d\d\d)')
>>> phoneNumRegex.search('My number is (415)-555-4242.').groups()	## groups()返回多个值的元组
('(415)', '555-4242')

用管道匹配多个分组:字符|称为“管道”,匹配许多表达式中的一个,可以理解成“或”。

>>> import re
>>> orRegex=re.compile(r'abc|efg hello world!')
>>> mo=orRegex.search('abc hello world')
>>> mo.group()
'abc'
>>> orRegex=re.compile(r'Ta|om')
>>> mo=orRegex.search('Tom is man')
>>> mo.group()
'om'
>>> mo=orRegex.search('Tam is man')
>>> mo.group()
'Ta'
>>> orRegex=re.compile(r'T(a|o)m')
>>> orRegex.search('Tam is man').group()
'Tam'

用问号实现可选匹配:字符?表明它前面的分组在这个模式中是可选的,零个或者一个。

>>> import re
>>> abcRegex=re.compile(r'abc(d)?efg')
>>> abcRegex.search('hello,abcefg')
<_sre.SRE_Match object; span=(6, 12), match='abcefg'>
>>> mo=abcRegex.search('hello,abcefg')
>>> mo.group()
'abcefg'
>>> mo.group(1)
>>> mo.groups()
(None,)
>>> mo=abcRegex.search('abcdefg')
>>> mo.group()
'abcdefg'
>>> mo.group(1)
'd'
>>> mo.groups()
('d',)
>>> mo.group(0)
'abcdefg'

用星号匹配零次或多次:*(称为星号)意味着“匹配零次或多次”,即星号之前的分组,可以在文本中出现任意次。

>>> import re
>>> abcRegex=re.compile(r'abc(d)*efg')
>>> mo=abcRegex.search('abcdddddefg')
>>> mo.group()
'abcdddddefg'
>>> mo.groups()
('d',)
>>> mo.group(1)
'd'
>>> abcRegex=re.compile(r'abc(d)*efg')
>>> mo=abcRegex.search('abcefg')
>>> mo.group()
'abcefg'

用加号匹配一次或多次:+(加号) 则意味着“匹配一次或多次”。 星号不要求分组出现在匹配的字符串中, 但加号不同, 加号前面的分组必须“至少出现一次”。

>>> import re
>>> abcRegex=re.compile(r'abc(d)+efg')
>>> mo=abcRegex.search('abcdddddefg')
>>> mo.group()
'abcdddddefg'

用花括号匹配特定次数:一个分组重复特定次数,就在正则表达式中该分组的后面,跟上花括号包围的数字。例如,正则表达式(Ha){3}将匹配字符串'HaHaHa'。

除了一个数字,还可以指定一个范围,即在花括号中写下一个最小值、一个逗号和一个最大值。例如,正则表达式(Ha){3,5}将匹配'HaHaHa'、 'HaHaHaHa'和'HaHaHaHaHa'。也可以不写花括号中的第一个或第二个数字, 不限定最小值或最大值。例如,

(Ha){3,}将匹配 3 次或更多次实例, (Ha){,5}将匹配 0 到 5 次实例。

>>> import re
>>> abcRegex=re.compile(r'abc(d){2,}efg')
>>> mo=abcRegex.search('abcddefg')
>>> mo.group()
'abcddefg'
>>> abcRegex.search('abcdefg')==None	## 无匹配返回None
True
>>> abcRegex=re.compile(r'abc(d){5}efg')
>>> abcRegex.search('abcdefg')==None
True
>>> abcRegex.search('abcdddddefg').group()
'abcdddddefg'
>>> abcRegex.search('abcdddddefg').group(1)
'd'

贪心和非贪心匹配:在字符串'HaHaHaHaHa'中,因为(Ha){3,5}可以匹配 3 个、 4 个或 5 个实例,Python 的正则表达式默认是“贪心” 的, 它们会尽可能匹配最长的字符串。花括号的“非贪心” 版本匹配尽可能最短的字符串,即在量词后跟一个问号。

>>> import re
>>> abcRegex=re.compile(r'(abc)+')	
>>> mo=abcRegex.search('abc'*5)	# abc连续5 'abc'个构成的字符串,1、2、3、4、5个连续都能被匹配到
>>> mo.group()
'abcabcabcabcabc'	# 默认贪心模式,匹配最多的情况,即5个连接的'abc'
>>> mo.group(1)
'abc'
>>> abcRegex=re.compile(r'(abc)+?')	# 改成非贪心模式
>>> mo=abcRegex.search('abc'*5)
>>> mo.group()
'abc'	# 匹配最小情况,即1个连接的'abc'
>>> mo.group(1)
'abc'

findall()方法:返回一组字符串, 包含被查找字符串中的所有匹配。search()返回的 Match 对象只包含第一次出现的匹配文本。

>>> import re
>>> phoneNumRegex=re.compile(r'\d{3}-\d{3}-\d{4}')
>>> mo=phoneNumRegex.search('Cell: 415-555-9999 Work: 212-555-0000')
>>> mo.group()	# 只能匹配第一次出现项
'415-555-9999'
>>> mo=phoneNumRegex.findall('Cell: 415-555-9999 Work: 212-555-0000')
>>> mo
['415-555-9999', '212-555-0000']	# 正则表达式中没有分组返回list列表
>>> phoneNumRegex=re.compile(r'(\d{3})-(\d{3}-\d{4})')	# 进行分组
>>> mo=phoneNumRegex.findall('Cell: 415-555-9999 Work: 212-555-0000')
>>> mo
[('415', '555-9999'), ('212', '555-0000')]	# 返回包含分组元组的列表

字符分类:

1. \d 表示 0 到 9 的任何数字

2. \D 表示 除 0 到 9 的数字以外的任何字符

3. \w 表示 任何字母、数字或下划线字符(可以认为是匹配“单词”字符)

4. \W 表示 除字母、数字和下划线以外的任何字符

5. \s 表示 空格、制表符或换行符(可以认为是匹配“空白”字符)

6. \S 表示 除空格、制表符和换行符以外的任何字符

插入字符和美元字符:插入符号(^),表明匹配开始处。类似地,在正则表达式的末尾加上美元符号($),表示该字符串这个模式结束。可以同时使用^和$。

>>> import re
>>> abcRegex=re.compile(r'^Hello')
>>> mo=abcRegex.search('abcHellodf')
>>> mo.group()
Traceback (most recent call last):
  File "", line 1, in 
	mo.group()
AttributeError: 'NoneType' object has no attribute 'group'
>>> mo=abcRegex.search('Hello World')
>>> mo.group()
'Hello'
>>> abcRegex=re.compile(r'\d$')
>>> mo=abcRegex.search('abc123')
>>> mo.group()
'3'

通配字符:.(句点)字符称为“通配符”。它匹配除了换行之外的所有字符。

>>> import re
>>> atRegex=re.compile(r'.at')
>>> mo=atRegex.findall('The cat in the hat sat on the flat mat.')
>>> mo
['cat', 'hat', 'sat', 'lat', 'mat']

>>> abcRegex=re.compile(r'.*')
>>> mo=abcRegex.search('abcdefg\nwerty')
>>> mo.group()
'abcdefg'

## 如果需要匹配换行,通过传入 re.DOTALL 作为 re.compile()的第
## 二个参数, 可以让句点字符匹配所有字符, 包括换行字符。
>>> abcRegex=re.compile(r'.*',re.DOTALL)
>>> mo=abcRegex.search('abcdefg\nwerty')
>>> mo.group()
'abcdefg\nwerty'

## 贪心与非贪心
>>> abcRegex=re.compile(r'<.*>')
>>> mo=abcRegex.search('<abc>defghi.>')
>>> mo.group()
'defghi.>'
>>> abcRegex=re.compile(r'<.*?>')
>>> mo=abcRegex.search('<abc>defghi.>')
>>> mo.group()
'<abc>'

不区分大小写的匹配:要让正则表达式不区分大小写,可以向 re.compile()传入 re.IGNORECASE 或 re.I,作为第二个参数。

>>> import re
>>> abcRegex=re.compile(r'abc',re.I)
>>> mo=abcRegex.search('AbC')
>>> mo.group()
'AbC'

用 sub()方法替换字符串:sub()方法需要传入两个参数。第一个参数是一个字符串, 用于取代发现的匹配字符串。第二个参数是原始字符串。 sub()方法返回替换完成后的字符串。

>>> import re
>>> abcRegex=re.compile(r'\d+')
>>> mo=abcRegex.search('phone:1341323456,tel:4323598')
>>> mo
<_sre.SRE_Match object; span=(6, 16), match='1341323456'>
>>> mo.group()
'1341323456'
>>> abcRegex.sub('#','phone:1341323456,tel:4323598')
'phone:#,tel:#'

管理复杂的正则表达式:对应长的、难以阅读的正则表达式,通过向 re.compile() 传入变量 re.VERBOSE, 作为第二个参数。忽略正则表达式字符串中的空白符和注释, 从而缓解这一点。

phoneRegex = re.compile(r'((\d{3}|\(\d{3}\))?(\s|-|\.)?\d{3}(\s|-|\.)\d{4}
							(\s*(ext|x|ext.)\s*\d{2,5})?)')
							
phoneRegex = re.compile(r'''(
	(\d{3}|\(\d{3}\))? 	# 匹配d{3}或(d{3}) 0次或一次
	(\s|-|\.)? 	# 分隔符:空白字符或-或. 0次或一次
	\d{3} 	# 3个数字
	(\s|-|\.) 	# 分隔符:空白字符或-或. 
	\d{4} 	# 4个数字
	(\s*(ext|x|ext.)\s*\d{2,5})? 	# extension
	)''', re.VERBOSE)

组合使用 re.IGNOREC ASE、 re.DOTALL 和 re.VERBOSE

someRegexValue = re.compile('foo', re.IGNORECASE | re.DOTALL | re.VERBOSE)