之前一直对正则表达式有点陌生,人对陌生的东西,自然有点恐惧。最近正在学习爬虫,看Ryan Mitchell的Web Scraping with Python, 里面有比较好的关于正则表达式的介绍,所以把它翻译下来,分享给大家。
Regular Expressions
有一句计算机科学界的笑话“你有一个问题,然后你决定用正则表达式去解决,好了,那么你有两个问题。”(看来大家对正则表达式的感受是一样的)。
不幸的是,正则表达式(regex)通常是用有很多乱七八糟符号的表格来教授的,看上去简直和胡扯一样,这容易令人生厌。然后当他们去工作的时候,本来可以用一行正则表达式搞定的东西,就会写一大堆没必要的搜索和过滤函数。
你很幸运,看到这本书,因为正则表达式并没有这么难,而且确实很高效,而且用几个例子就能很快的学会。
正则表达式之所以叫正则表达式(中文翻译的总是这么邪乎),就是因为它是用来识别有规律的字符串(regular strings)。也就是说,给定正则表达式,对于文本来说,要么“满足规律,我返回”,要么“不满足规律,我不返回”。这在大文本中寻找比如像电话号码或者邮件地址这样的字符串的时候让人感觉异常方便。
注意到我说regular strings,那么什么样的字符串算是有规律的字符串?它是指任何可以通过一系列线性规则生成的字符串,比如:
- 至少有一个“a”
- 然后跟5个“b”
- 跟偶数个“c”
- 结尾可能以“d”结尾
满足这些条件的字符串有“aaaabbbbbccccd”,“aabbbbbcc”等等(总共有无限种变体)。
正则表达式其实就是这些规则的缩写。比如,下面这个就是上面4个规则的正则表达式。
aa*bbbbb(cc)*(d | )
这个字符串一看上去有点慌,但是当我们对它进行分解的时候,也就变得简单了。
aa*
先写一个a,然后后面跟这a*,a*的意思是任意个数的a,包括0个从而保证至少有一个a。
bbbbb
就是5个b。
(cc)*
任何偶数次的东西可以称之为对,所以这里要达到偶数次的c可以写两个c,然后用括号括起来,后面跟一个星号,星号意味着可以有任何数量的c对,这里同样可以是0对。
(d | )
“|”这个的意思就是“或”,在这里就是“d ”或“ ”,这样我们就可以保证最多一个d结尾。
正则表达式的一个经典的例子是识别一个邮件地址。虽然对于不同的邮件服务器来说,邮件地址的规则可能存在细微的不同,但我们还是可以发现一些通用的规则。对应的正则表达式出现在表格的第二列。
规则 | 正则表达式 |
---|---|
规则1:邮件地址至少包含以下字符中的一个:大写字母、小写字母、数字0-9、英文句号、加号、下划线 | [A-Za-z0-9.+_]+:中括号的意思是可以是中括号中的任意一个符号,加号的意思是这个符号或表达式可以出现任意次数,但至少要出现一次 |
规则2:有一个“@” | @:在中间有且仅出现一次 |
规则3:必须跟至少一个大写或小写字母 | [A-Za-z]+ |
规则4:必须有一个英文句号 | . |
规则5:地址以org,com,edu,或者net结尾(现实中还有很多,这里只举例) | (org |com |edu |net) |
连接所有的规则我们就得到一个正则表达式:
[A-Za-z0-9+_.]+@[A-Za-z]+.(org |com |edu |net)
当要的写一个正则表达式的时候,最好先列一个正则表达式的大纲,列出表达式所暗含的字符串规则。注意特例,比如你是用来识别电话号码,你考虑了国家代码了吗?
最后,下表列出了常见的正则表达式符号,以及简要的介绍和相应的例子。这个表格绝对不完整,而且随着语言的不同可能也有轻微的差别。无论如何这12个符号在Python中最常用,他们可以用来查找和收集大部分的字符串。
符号 | 意义 | 例子 | 实际例子 |
---|---|---|---|
* | 匹配前面的字符,子表达式,或者中括号字符,0次或多次 | a*b* | aaaaa,bbbbb,aabb |
+ | 匹配前面的字符,子表达式,或者中括号字符,1次或多次 | a+b+ | aaab,abbb,aaaabbbb |
[] | 匹配中括号中的任意字符 | [A-Z]* | APPLE,CAPITALS,QWERTY |
() | 子表达式(这在正则表达式检查顺序中是第一位的) | (a*b)* | aaabab,aaab,abab |
{m,n} | 匹配前面的字符,子表达式,或者中括号字符m-n次(闭区间) | a{2,3}b{2,3} | aabbb,aaabbb,aabb |
[^] | 不匹配中括号中任意一个字符 | [^A-Z]* | apple,lowcase,qwerty |
| | 匹配任何一个由“|”分隔开的字符,字符串,或者子表达式 | b(a|i|e)d | bad,bid,bed |
. | 匹配任意单个字符(包括字符,数字或空格等) | b.d | bad,bed,b d,b$d |
^ | 表明一个字符或者子字符串出现在整个字符串的开头 | ^a | apple, abandon,adaf |
\ | 转义字符(它使得你可以使用特殊字符的字面形式) | \.\|\\ | .|\ |
$ | 放在正则表达式末尾,表示这里是表达式的末尾。如果没有这个符号,任意一个表达式在末尾都相当于有一个默认的“.*” | [A-Z]*[a-z]*$ | ABCabc,zzzyx,Bob |
?! | 不包含。这个奇怪的字符对出现在一个字符或者子表达式前面,表明这个字符或者子表达式不能出现在该字符串中的该位置上。这用起来要小心,因为它并不排除该字符或者子表达式出现在字符串的其它位置上。如果要在字符串中完全消除某个字符或者子表达式,需要在两端用^和$配合使用。 | ^((?![A -Z]).)*$ | no-caps-here, joewjjige |
参考文献: Web Scraping with Python by Ryan Mitchell (O’Reilly). Copyright 2015 Ryan Mitchell, 978-1-491-91029-0, p22.