Regular Expression is all about the patterns within string, which forms a language of logic.
1. 读者须知
这篇文章只能介绍以下入门级知识:
- 正则表达式的思维方式
- 正则表达式的相关概念
- 如何写出正确的正则表达式
- re — Python Build-in Module
这篇文章不能为你介绍正则表达式的历史或演进、底层编译逻辑等内容。
2. 简介
正则表达式(Regular Expression, regex)可以认为一种小型的计算机语言,用于表达某种字符(串)模式。我个人第一次学习的时候,感觉到它要求使用者有严格的逻辑。希望读者在文章的最后能感觉到,这篇文章使用了正则表达式的逻辑来介绍正则表达式。
我觉得正则表达式是一种必需品。每一个代码家都应该学会它,至少有所了解,并能写出简单的正则表达式匹配某些常见模式。说它必不可少,是因为在和字符串打交道的时候,固定的函数几乎不可能满足我们的“处理刚需”。
举个例子,找出电话本中所有名字含有“叶”的人名。它可能是姓“叶”,或者第二个字、第三个字是“叶”。这种情况比较简单,由于中文名字的长度非常有限,代码多啰嗦几行,便能完成任务。
更符合人类思维方式的做法是:直接显式地指明这个模式(pattern)——“叶”字前后均有可能有字符(串)的人名。比如叶青,麻仓叶等。极端的例子是:叶叶叶叶。
Where there is a pattern, there is a regex.
3. regex相关概念
3.1 字符集
正则表达式中的主体是:字符集 + 数量词。字符集用于表明哪些字符会参与模式匹配,数量词表示匹配的次数。在上面的例子中,“叶”是我们唯一需要匹配的字符,而它在人名(被匹配的字符串文本)中几乎只会出现1次。所以数量词的限制也是处理过程中的刚需。也就是说,我们不希望“叶叶”或“叶叶叶”出现在最后的结果中。
[….]
表示一个用于匹配的自定义字符集,意味着对应位置可以是字符集中的任一字符。a[bcd]e
代表了abe,ace,ade三种可能,也可以写成a[b-d]e
。
字符集中,我们可以使用范围来表示一堆字符。
[a-z]
表示所有小写字母,[0-9]
表示数字字符集[0123456789]
。
范围可以堆砌。比如使用[a-zA-Z0-9]
表示所有的大小写字母以及数字。而常用的字符集被预先定义好了,优秀的程序猿都是很贴心的。
预定义字符集 | 含义 | 等价字符集 |
---|---|---|
\d |
数字 | [0-9] |
\D |
非数字 | [^\d] |
\s |
各种空白字符 | [ \t\r\n\f\v] |
\S |
非空白字符 | [^\s] |
\w |
单词字符 | [A-Za-z0-9_] |
\W |
非单词字符 | [^\w] |
观察上表,^
放在字符集的最前表示取反。大写的均为反义。建议背下来。
3.2 数量词
上文已经提到“叶叶叶叶”,暗示正则表达式强大之处在于匹配不定长的字符串。定制这个需求依赖于数量词。
数量词 | 含义 |
---|---|
* |
匹配前一个字符0或无限次 |
+ |
匹配前一个字符1或无限次 |
? |
匹配前一个字符0或1次 |
{m} |
匹配前一个字符m次 |
{m,n} |
匹配前一个字符m到n次 |
举例:姓“叶”的或姓“王”的,写成[叶王]{1}
。不适合写成[叶王]?
,因为可能是0次出现。这是危险的。假设有叶子,叶问,叶儿,王儿,海儿,并想找出姓“叶”或“王”,名为“儿”的所有名字,写成[叶王]{1}儿
是正确的,但是[叶王]?儿
则会多出一个“海儿”的结果。
到此,读者应该体会到正则表达式是一种关于逻辑的表达,用于过滤意料之外的情况。如果考虑不周,则可能得到意想不到的结果。更多详细介绍参见文末附表。
3.3 贪婪模式
正则表达式在Python中默认是贪婪的。例如,aa<div>bb</div>cc<div>dd</div>ee
中,我们关心<div>
和 </div>
之间的信息bb和dd。
如果使用<div>.*</div>
会返回bb</div>cc<div>dd
.(’.’可以匹配任意出换行符之外的字符)。
如果需要关闭贪婪模式,需要在数量词后紧接一个'?'
,即<div>.*?</div>
,此时返回bb。
3.4 转义
与大多数编程语言相同,正则表达式里使用\
作为转义字符,这就可能造成反斜杠困扰。
假如你需要匹配文本中的字符\
,那么使用编程语言表示的正则表达式里将需要4个反斜杠\\\\
:前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。
Python里的原生字符串很好地解决了这个问题,我们可以在字符串前缀r
,来显式声明这是一个正则表达式。上面的例子中,正则表达式\\\\
等价于r"\\"
。同样,匹配一个数字的\\d
可以写成r"\d"
。有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
4. re模块简介
4.1 re.compile(pattern, flags=0)
预先将正则表达式编译为RegexObject,如需在循环中多次调用,预编译可以节省时间。
|
|
第二个参数flag是匹配模式,取值可以使用按位或运算符’|’表示同时生效,比如re.I | re.M。这里尚未研究。但re.X
比较实用,指详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。
|
|
可以提高表达式可读性,也可以方便coder自行拆分复杂表达式,确保不会犯逻辑错误。
4.2 re.match(pattern, string, flags=0)
|
|
4.3 re.sub(pattern, repl, string, count=0, flags=0)
重要参数的含义依次为:匹配的模式,用于替换的字符串,原字符串。
|
|