8 正则表达式

正则表达式

正则表达式使用某种预定义的模式去匹配一类具有共同特征的字符串,它能帮助你方便的检查一个字符串是否与某种模式匹配。主要用于处理字符串,可以快速、准确地完成复杂的查找、替换等处理要求,在文本编辑与处理、网页爬虫之类的场合中有重要应用,例如在大的文本中搜索一段字段。

Python中,re模块提供了正则表达式操作所需要的功能。

正则表达式语法

普通字符:没有什么含义,可以接匹配。

特殊的字符,术语叫 metacharacters(元字符)。例如:. * + ? \ [ ] ^ $ { } | ( )

元字符功能说明
.匹配除换行符之外的任意单个字符
*匹配前面的子表达式任意次数,包括0次,也是匹配位于*之前的字符或子模式的0次或多次出现(匹配任意次)
+匹配前面的子表达式一次或多次数,匹配位于+之前的字符或子模式的1次或多次出现
-在[]之内用来表示范围
|匹配位于|之前或之后的字符,匹配其中之一
^匹配文本起始位置,匹配行首,匹配以^后面的字符开头的字符串, 单行模式 ,表示匹配 整个文本 的开头位置, 多行模式 ,表示匹配 文本每行 的开头位置。^\d+
$匹配行尾,匹配以$之前的字符结束的字符串,单行模式 ,表示匹配 整个文本 的结尾位置。如果是 多行模式 ,表示匹配 文本每行 的结尾位置。\d+$
?匹配位于?之前的0个或1个字符。当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后时,匹配模式是“非贪心的”。“非贪心的”模式匹配搜索到的、尽可能短的字符串,而默认的“贪心的”(尽可能多的)模式匹配搜索到的、尽可能长的字符串。例如,在字符串“oooo”中,“o+?”只匹配单个“o”,而“o+”匹配所有“o”
\表示位于\之后的为转义字符,匹配某种字符类型,查找元字符,就要使用\,例如:.*\.
\num此处的num是一个正整数,表示子模式编号。
例如,“(.)\1”匹配两个连续的相同字符
\f换页符匹配
\n换行符匹配
\r匹配一个回车符
\b匹配单词头或单词尾
\B与\b含义相反
\d匹配任何数字,相当于[0-9]
\D与\d含义相反,等效于[^0-9]
\s匹配任何空白字符,包括空格、制表符、换页符,与 [ \f\n\r\t\v] 等效
\S与\s含义相反
\w匹配文字字符,任何字母、数字以及下划线,相当于[a-zA-Z0-9_]
\W与\w含义相反\w含义相反,与“[^A-Za-z0-9_]”等效
()组选择,将位于()内的内容作为一个整体来对待
{m,n}{}前的字符或子模式重复至少m次,至多n次
[]表示范围,匹配位于[]中的任意一个字符,要匹配 指定的几个字符之一 ,也可以是范围。
一些元字符在方括号内失去了魔法,变得和普通字符一样了,例如[akm.]、[abc]
[^xyz]反向字符集,匹配除x、y、z之外的任何字符
[a-z]字符范围,匹配指定范围内的任何字符
[^a-z]反向范围字符,匹配除小写英文字母之外的任何字符

正则表达式的先行断言和后行断言

正则表达式的先行断言和后行断言一共有 4 种形式:

  1. (?=pattern) 零宽正向先行断言(zero-width positive lookahead assertion)
  2. (?!pattern) 零宽负向先行断言(zero-width negative lookahead assertion)
  3. (?<=pattern) 零宽正向后行断言(zero-width positive lookbehind assertion)
  4. (?<!pattern) 零宽负向后行断言(zero-width negative lookbehind assertion)

这里面的  pattern 是一个正则表达式。

如同  ^ 代表开头, $ 代表结尾, \b 代表单词边界一样,先行断言和后行断言也有类似的作用,它们只匹配某些位置,在匹配过程中,不占用字符,所以被称为 "零宽"。所谓位置,是指字符串中(每行)第一个字符的左边、最后一个字符的右边以及相邻字符的中间(假设文字方向是头左尾右)。

子模式编号\num

此处的num是一个正整数,表示子模式编号。
例如,“(.)\1”匹配两个连续的相同字符

re模块主要函数

方法功能说明
compile(pattern[, flags])创建正则表达式对象
escape(string)将字符串中所有特殊正则表达式字符转义
findall(pattern, string[, flags])返回包含字符串中所有与给定模式匹配的项的列表
finditer(pattern, string, flags=0)返回包含所有匹配项的迭代对象,其中每个匹配项都是Match对象
fullmatch(pattern, string, flags=0)尝试把模式作用于整个字符串,返回Match对象或None
match(pattern, string[, flags])从字符串的开始处匹配模式,返回Match对象或None
purge()清空正则表达式缓存
search(pattern, string[, flags])在整个字符串中寻找模式,返回Match对象或None
split(pattern, string[, maxsplit=0])根据模式匹配项分隔字符串
sub(pat, repl, string[, count=0])将字符串中所有与pat匹配的项用repl替换,返回新字符串,repl可以是字符串或返回字符串的可调用对象,作用于每个匹配的Match对象
subn(pat, repl, string[, count=0])将字符串中所有pat的匹配项用repl替换,返回包含新字符串和替换次数的二元元组,repl可以是字符串或返回字符串的可调用对象,作用于每个匹配的Match对象

flag参数常用值及含义

re.A:使得正则表达式中\w、\W、\b、\B、\d、\D、\s和\S等元字符只匹配ASCII字符,不匹配Unicode字符。

>>> import re
>>> re.findall('\d+', '1231234')
['1231234']
>>> re.findall('\d+', '1231234', re.A)
['123']
>>> re.findall('\w+', '1a2b3c1d2e3g4', re.A)
['1a2b3c', 'd', 'e', 'g']
>>> re.findall('\w+', '1a2b3c1d2e3g4')
['1a2b3c1d2e3g4']

re.I:忽略大小写。

>>> re.findall('[a-z0-9]+', '1a2b3c1D2e3G4')
['1a2b3c', 'e']
>>> re.findall('[a-z0-9]+', '1a2b3c1D2e3G4', re.I)
['1a2b3c', 'D', 'e', 'G']
>>> re.findall('[a-z0-90-9]+', '1a2b3c1D2e3G4', re.I)
['1a2b3c1D2e3G4']
>>> re.findall('[a-z0-90-9]+', '1a2b3c1D2e3G4')
['1a2b3c1', '2e3', '4']

re.M:多行模式,^可以匹配每行开始,$可以匹配每行结束。默认情况下分别匹配字符串的开始和结束。

>>> text = '''
abc1234
1234
abc
Python
董付国
'''
>>> re.findall(r'^\w+$', text)
[]
>>> re.findall(r'^.+$', text)
[]
>>> re.findall(r'^\w+$', text, re.M)
['abc1234', '1234', 'abc', 'Python', '董付国']
>>> re.findall(r'^.+$', text, re.M)
['abc1234', '1234', 'abc', 'Python', '董付国']

re.S:点任意匹配模式,包括换行符,改变'.'的行为

>>> text = '''

Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex.

''' >>> re.findall(r'

(.+?)

', text) [] >>> re.findall(r'

(.+?)

', text, re.S) ['Beautiful is better than ugly.\nExplicit is better than implicit.\nSimple is better than complex.']
>>> text = '''
good
bad
345a
abc456
'''
>>> re.findall(r'\w+', text)
['good', 'bad', '345a', 'abc456']
>>> re.findall(r'^\w+$', text)         #\w不能匹配换行符,^匹配整个字符串的开始
[]
>>> re.findall(r'^.+$', text)          #圆点也不能匹配换行符,$匹配整个字符串的结束
[]
>>> re.findall(r'^.+$', text, re.S)    #单行模式,此时圆点可以匹配换行符
['\ngood\nbad\n345a\nabc456\n']
>>> re.findall(r'^.+$', text, re.M)    #多行模式,^和$可以匹配每一行的开始和结束
['good', 'bad', '345a', 'abc456']

re.X:允许正则表达式换行,并忽略其中的空白字符和#注释。

>>> text = 'abc123.4dfg8.88888hij9999.9'
>>> pattern = r'''\d+     # 数字
\.                        # 圆点
\d +'''
>>> re.findall(pattern, text)
[]
>>> re.findall(pattern, text, re.X)
['123.4', '8.88888', '9999.9']

多个flag可以使用+组合使用。

>>> text = '''abc123.4d
fg8.88888hi
j9999.9
000.00
asdf'''
>>> pattern = r'''^\d+     # 数字
\.                         # 圆点
\d +$'''
>>> re.findall(pattern, text)
[]
>>> re.findall(pattern, text, re.M)
[]
>>> re.findall(pattern, text, re.X)
[]
>>> re.findall(pattern, text, re.X+re.M)
['000.00']

直接使用re模块函数

>>> import re                            #导入re模块
>>> text = 'alpha. beta....gamma delta'  #测试用的字符串
>>> re.split('[\. ]+', text)             #使用指定字符作为分隔符进行分隔
['alpha', 'beta', 'gamma', 'delta']
>>> re.split('[\. ]+', text, maxsplit=2) #最多分隔2次
['alpha', 'beta', 'gamma delta']
>>> re.split('[\. ]+', text, maxsplit=1) #最多分隔1次
['alpha', 'beta....gamma delta']
>>> pat = '[a-zA-Z]+'
>>> re.findall(pat, text)                #查找所有单词
['alpha', 'beta', 'gamma', 'delta']

>>> import re                            #导入re模块
>>> text = 'alpha. beta....gamma delta'  #测试用的字符串
>>> re.split('[\. ]+', text)             #使用指定字符作为分隔符进行分隔
['alpha', 'beta', 'gamma', 'delta']
>>> re.split('[\. ]+', text, maxsplit=2) #最多分隔2次
['alpha', 'beta', 'gamma delta']
>>> re.split('[\. ]+', text, maxsplit=1) #最多分隔1次
['alpha', 'beta....gamma delta']
>>> pat = '[a-zA-Z]+'
>>> re.findall(pat, text)                #查找所有单词
['alpha', 'beta', 'gamma', 'delta']

使用正则表达式对象

首先使用re模块的compile()方法将正则表达式编译生成正则表达式对象,然后再使用正则表达式对象提供的方法进行字符串处理。

使用编译后的正则表达式对象可以提高字符串处理速度,也提供了更强大的文本处理功能。

  • 正则表达式对象的match(string[, pos[, endpos]])方法用于在字符串开头或指定位置进行搜索,模式必须出现在字符串开头或指定位置; 正则表达式对象的search(string[, pos[, endpos]])方法用于在整个字符串中进行搜索; 正则表达式对象的findall(string[, pos[, endpos]])方法用于在字符串中查找所有符合正则表达式的字符串并返回列表。
  • 正则表达式对象的sub(repl, string[, count = 0])和subn(repl, string[, count = 0])方法用来实现字符串替换功能,其中参数repl可以为字符串或返回字符串的可调用对象。
  • 正则表达式对象的split(string[, maxsplit = 0])方法用来实现字符串分隔。
-->