正则表达式

时间:2024-02-16 10:45:25

1  正则表达式到底是什么东西?

在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。

很可能你使用过Windows/Dos下用于文件查找的通配符(wildcard),也就是 * 和 ? 。如果你想查找某个目录下的所有的Word文档的话,你会搜索 *.doc。在这里,* 会被解释成任意的字符串。

和通配符类似,正则表达式也是用来进行文本匹配的工具,只不过比起通配符,它能更精确地描述你的需求——当然,代价就是更复杂——比如你可以编写一个正则表达式,用来查找所有以0开头,后面跟着2-3个数字,然后是一个连字号 "-",最后是7或8位数字的字符串(像010-12345678或0376-7654321)。

2  从几个例子开始

学习正则表达式的最好方法是从例子开始,理解例子之后再自己对例子进行修改,实验。

例一:在一篇英文小说里查找 hi,你可以使用正则表达式 hi。

这几乎是最简单的正则表达式了,它可以精确匹配这样的字符串:由两个字符组成,前一个字符是h,后一个是 i。通常,处理正则表达式的工具会提供一个忽略大小写的选项,如果选中了这个选项,它可以匹配 hi,HI,Hi,hI这四种情况中的任意一种。

不幸的是,很多单词里包含 hi 这两个连续的字符,比如him,history,high等等。用 hi 来查找的话,这里边的 hi 也会被找出来。如果要精确地查找hi这个单词的话,我们应该使用 \bhi\b。

\b 是正则表达式规定的一个特殊代码(好吧,某些人叫它元字符,metacharacter),代表着单词的开头或结尾,也就是单词的分界处。虽然通常英文的单词是由空格、标点符号或者换行来分隔的,但是 \b 并不匹配这些单词分隔字符中的任何一个,它只匹配一个位置

例二:假如你要找的是 hi 后面不远处跟着的一个Lucy,你应该用 \bhi\b.*\bLucy\b。

这里,' . '是另一个元字符,匹配除了换行符以外的任意字符。' * '同样是元字符,不过它代表的不是字符,也不是位置,而是数量——它指定' * '前边的内容可以连续重复使用任意次以使整个表达式得到匹配。因此,' .* '连在一起就意味着任意数量的不包含换行的字符。现在 \bhi\b.*\bLucy\b 的意思就很明显了:先是一个单词hi,然后是任意个任意字符(但不能是换行),最后是Lucy这个单词。

如果同时使用其它元字符,我们就能构造出功能更强大的正则表达式。

例三:0\d\d\d-\d\d\d\d\d\d\d 匹配这样的字符串

import re

r = r'0\d\d\d-\d\d\d\d\d\d\d'
m = re.search(r,'秦栏小学的电话号码是:0550-7811354,请联系。')
print(m)

# 结果:<re.Match object; span=(11, 23), match='0550-7811354'>

以0开头,然后是三个数字,然后是一个连字号"-",最后是7个数字(也就是中国的电话号码。当然,这个例子只能匹配区号为4位的情形)。

这里的 \d 是个新的元字符,匹配一位数字(0~9)。- 不是元字符,只匹配它本身——连字符(或者减号,或者中横线,或者随你怎么称呼它)。

为了避免那么多烦人的重复,我们也可以这样写这个表达式:0\d{3}-\d{7}。这里\d后面的{3}({7})的意思是前面\d必须连续重复匹配3次(7次)。

3  测试正则表达式

正则表达式的语法很令人头疼,即使对经常使用它的人来说也是如此。由于难于读写,容易出错,所以找一种工具对正则表达式进行测试是很有必要的。

不同的环境下正则表达式的一些细节是不相同的,这里介绍两种可用的测试工具:

  • RegexBuddy
  • Javascript正则表达式在线测试工具

4  元字符

现在你已经知道几个很有用的元字符了,如 ' \b', ' . ','  * ' 还有 ' \d '。正则表达式里还有更多的元字符,比如 ' \s '匹配任意的空白符,包括空格,制表符(Tab),换行符,中文全角空格等。' \w ' 匹配字母或数字或下划线或汉字等。

例一:\ba\w*\b 匹配以字母a开头的单词

先是某个单词开始处(\b),然后是字母a,然后是任意数量的字母或数字(\w*),最后是单词结束处(\b)。

例二:\d+ 匹配1个或更多连续的数字

这里的 + 是和 * 类似的元字符,不同的是 * 匹配重复任意次(可能是0次),而 + 则匹配重复1次或更多次。

例三:\b\w{6}\b 匹配刚好6个字符的单词。

表1.常用的元字符
代码 说明
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或结束
^ 匹配字符串的开始
$ 匹配字符串的结束

 5  字符转义

如果你想查找元字符本身的话,比如你查找' . ',或者' * ',就出现了问题:你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用 \ 来取消这些字符的特殊意义。因此,你应该使用 \.\*。当然,要查找 \ 本身,你也得用 \\ 。

例如:deerchao\.net匹配deerchao.net,C:\\Windows匹配C:\Windows。

6  重复

你已经看过了前面的 * , + , {2} , {5,12} 这几个匹配重复的方式了。下面是正则表达式中所有的限定符(指定数量的代码):

表2.常用的限定符
代码/语法 说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

 

模式字符串使用特殊的语法来表示一个正则表达式。

字母和数字表示他们自身。一个正则表达式模式中的字母和数字匹配同样的字符串。

多数字母和数字前加一个反斜杠时会拥有不同的含义。

标点符号只有被转义时才匹配自身,否则它们表示特殊的含义。

反斜杠本身需要使用反斜杠转义。

由于正则表达式通常都包含反斜杠,所以你最好使用原始字符串来表示它们。模式元素(如 r'\t',等价于 \\t )匹配相应的特殊字符。

下表列出了正则表达式模式语法中的特殊元素。如果你使用模式的同时提供了可选的标志参数,某些模式元素的含义会改变。

模式 描述
^ 匹配字符串的开头
$ 匹配字符串的末尾。
. 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。
[...] 用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k'
[^...] 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。
re* 匹配0个或多个的表达式。
re+ 匹配1个或多个的表达式。
re? 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
re{ n} 匹配n个前面表达式。例如,"o{2}"不能匹配"Bob"中的"o",但是能匹配"food"中的两个o。
re{ n,} 精确匹配n个前面表达式。例如,"o{2,}"不能匹配"Bob"中的"o",但能匹配"foooood"中的所有o。"o{1,}"等价于"o+"。"o{0,}"则等价于"o*"。
re{ n, m} 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
a| b 匹配a或b
(re) 匹配括号内的表达式,也表示一个组
(?imx) 正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。
(?-imx) 正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。
(?: re) 类似 (...), 但是不表示一个组
(?imx: re) 在括号中使用i, m, 或 x 可选标志
(?-imx: re) 在括号中不使用i, m, 或 x 可选标志
(?#...) 注释.
(?= re) 前向肯定界定符。如果所含正则表达式,以 ... 表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩余部分还要尝试界定符的右边。
(?! re) 前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功。
(?> re) 匹配的独立模式,省去回溯。
\w 匹配数字字母下划线
\W 匹配非数字字母下划线
\s 匹配任意空白字符,等价于 [\t\n\r\f]。
\S 匹配任意非空字符
\d 匹配任意数字,等价于 [0-9]。
\D 匹配任意非数字
\A 匹配字符串开始
\Z 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。
\z 匹配字符串结束
\G 匹配最后匹配完成的位置。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
\B 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
\n, \t, 等。 匹配一个换行符。匹配一个制表符, 等
\1...\9 匹配第n个分组的内容。
\10 匹配第n个分组的内容,如果它经匹配。否则指的是八进制字符码的表达式。