处理文本和数据是件大事,在自然语言处理、文本处理等方面都离不开正则表达式。正则表达式为高级文本模式匹配,以及搜索替代等功能提供了基础。正则表达式是一些由字符和特殊符号组成的字符串,它们描述了这些字符和字符的某种重复方式,因此能按某种模式匹配一系列有相似特征的字符串。
正则表达式使用的特殊符号
用管道符号(|)匹配多个正则表达式模式
管道符号“|”表示一个或操作,它的意思是选择被管道符号分隔的多个不同的正则表达式中的一个。 例如:
at | home 匹配at,home |
bat | bet | bit 匹配bat,bet,bit |
匹配任意一个单个的字符
点号“.”匹配出换行符以外的任意一个字符。无论书数字还是字母、空白符、可打印字符、非可打印字符都可以被它匹配。 例如:
f.o 在f和o之间有任何字符都可以被匹配,foo,fa0,fio,f9o,f o……
.. 匹配任意两个字符
从字符串的开头或结尾或单词边界开始匹配
脱字符号^用来匹配字符串开头,$符号用来匹配字符串结尾,例如:
^Form 匹配任何以Form开头的字符串
/bin/tcsh$ 匹配任何以/bin/tcsh结尾的字符串
^subject:hi$ 仅匹配subject:hi
特殊字符\b和\B用来匹配单词边界,两者之间的区别是:\b匹配的模式是一个单词边界,就是说与之对应的模式一定是在一个单词的开头,不论这个单词的前面是有字符(该词在一个字符串的中间),还是没有字符(该词在一行的起始处);而\B只是匹配出现在一个字符串中间的模式。例如:
the 匹配任何包含’the’的字符串
\bthe 匹配任何以the开始的字符串
\bthe\b 匹配单词’the’
\Bthe 匹配任意包含the但不以the开头的单词
“\b”可以表示字母在单词的边界,匹配\w和\W之间的位置,“\B”可以表示字母不在单词的边界,匹配[^\b]的位置。所谓\w和\W之间是指相邻的两个字符不全是\w字符(字母、数字、下划线)。
创建字符类
[]用来匹配某些个特殊的字符 例如:
b[aeiou]t 可以匹配bat,bet,bit,bot,but
指定范围和否定
方括号除了匹配单个字符外,还可以支持所指定的字符范围,方括号里使用一对符号中间的连字符(-)来表示一个字符的范围。另外,如果在左方括号后第一个字符是(^),就表示排除字符集里的任意字符。
[r-u][env-y][us] 匹配第一个字符是’r’,’s’,’t’,’u’中的任意一个,第二个字符是’e’,’n’,’v’,’w’,’x’,’y’中的任意一个,最后是字符u或s。
[^aeiou] 匹配非元音字符
使用闭包操作符实现多次出现/重复匹配
闭包操作符,即特殊符号“*”、“+”和“?”,它们可以用来匹配字符串模式出现一次、多次或未出现的情况。花括号操作符({}),花括号里可以是单个值,{M}表示匹配M次出现;也可以是一对逗号分开的值,{M,N}表示匹配M次到N此出现。
注意问号有两种含义:
- 单独使用时表示匹配0次或1次的情况
- 紧跟在表示重复的元字符后面时,表示要求搜索引擎匹配的字符串越短越好
越短越好是什么意思呢?当使用了表示重复的元字符(*+?{m,n})时,正则表达式引擎在匹配模式时会吸收尽可能多的字符,就、这就叫做“贪心”。问号告诉正则表达式引擎要求当前匹配消耗的字符越少越好,留下尽可能多的字符给后面的模式。
例子:
[dn]ot? 匹配do,no,dot.not,也就是第一个字符是d或n,第二个是o,最后一个是没有或者一个t
</?[^>]+> 匹配所有合法的HTML标签
特殊字符表示、字符集
有一些特殊字符可以用来代表字符集合。例如你可以不用“0-9”来表示十进制数字,而改用简写“\d”表示。类似地特殊字符还有“\w”表示整个字符数字的字符集,即等价于“A-Za-z0-9_”;“\s”表示空白字符。这些特殊字符的大写形式表示互斥,比如,“\D”表示非十进制数字的字符,等价于[^0-9]。
例子:
\w+-\d+ 匹配一个由字母或数字组成的字符串和至少一个数字,两部分中间用‘-’连接
\d{3}-\d{3}-\d{4} 匹配美国电话号码,例如800-555-1212
\w+@\w+.com 匹配简单的Email地址
用小括号()组建组
有时候我们也许对匹配的数据本身更感兴趣,比如,我们不仅想知道是否整个字符串匹配我们的条件,还想在匹配成功时取出某个特定的字符串或子字符串。要达到这个目的,只需要将正则表达式用“()”括起来。 一对括号和正则表达式一起使用时可以实现下面功能之一:
- 对正则表达式进行分组
- 匹配子组
使用括号的一个额外好处是匹配的字串会被保存到一个子组,便于今后使用。这些子组可以在同一次匹配或搜索中被重复使用,或被提取出来做进一步处理。
例子:
\d+(.\d*)? 表示简单的浮点型
正则表达式和Python
Python的默认正则表达式模块是re模块。
re模块:核心函数和方法
先来学习一下两个主要的函数match()和search()。
match(pattern,string,flags=0)
尝试用正则表达式pattern来匹配字符串string,flags是可选标记,如果匹配成功,则返回一个匹配对象;否则返回None。
search(pattern,string,flags=0)
在字符串string中搜索正则表达式模式pattern的第一次出现,flags是可选标记,如果匹配成功,则返回一个匹配对象;否则返回None。
二者的区别:
match()函数尝试从字符串的开头开始对模式进行匹配。如果匹配成功,就返回一个匹配对象,匹配失败就返回None。匹配对象的group()方法可以用来显示那个成功的匹配。
其实你要搜索的模式出现在字符串中间的几率要比出现在字符串开头的几率更大一些。search()函数和match的工作方式一样,不同之处在于search会检查参数字符串在任意位置的地方给定正则表达式模式的匹配情况。如果搜索到成功的匹配,就会返回一个匹配对象,否则返回None。
例子:
m = re.match('foo', 'seafood') 匹配失败
m = re.search('foo', 'seafood') 匹配成功
重复、特殊字符和子组
正则表达式中最常见的情况包括特殊字符的使用,正则表达式模式的重复出现,以及使用圆括号对匹配模式的各部分进行分组和提取操作。下面就通过一些例子来说明。
例子:
- 匹配一个Email地址,要求:支持主机名和域名,其中主机名是可选的,比如,xxx@www.sina.com,或者xxx@sina.com patt = ‘\w+@(\w+.)?\w+.com’ ?表示前面的分组模式出现0次或1次 re.match(patt,’nobody@xxx.com’)
- 有这样一个字符串,它由两部分组成,第一部分是3个字母,第二部分是三个数字,中间用‘-’分隔,试分别取出字母部分和数字部分
patt = '([a-zA-Z]{3})-(\d{3})'
m = re.match(patt,'abc-123')
m.group() #所有匹配部分
#'abc-123'
m.group(1) #匹配的子组1
#'abc'
m.group(2) #匹配的子组2
#'123'
m.groups() #匹配的所有子组
#('abc','123')
用findall()找到每个出现的匹配部分
findall和search的相似之处在于二者都执行字符串搜索,但findall和match与search不同之处在于findall总是返回一个列表。如果findall没有找到匹配的部分,会返回空列表;如果成功找到匹配部分,则返回所有匹配部分的列表(按从左到右出现的顺序排列)。
当正则表达式包含子组时,搜索会返回一个更复杂的列表:正则表达式仅有一个子组时,findall()返回子组匹配的字符串组成的列表;如果表达式有多个子组,返回的结果是一个元组的列表,元组中每一个元素都是一个子组的匹配内容。 比如:
re.findall(r'(a)(b)(c)','abcd')
#返回结果:[('a','b','c')]
用sub和subn进行搜索和替换
二者功能几乎一样,都是讲将某字符串中所有匹配正则表达式模式的部分进行替换。不同之处在于:subn还返回一个表示替换次数的数字,替换后的字符串和替换次数一起作为一个元组返回。