参考 python核心编程
操作字符串是件大事,因为,在编程中涉及到的最多的数据结构非字符串莫属了。
正则表达式(regex),为高级的文本模式匹配、抽取、与/或文本形式的搜索和替换功能提供了基础。
解释:正则表达式,是一些由字符和特殊符号组成的字符串,它描述了模式的重复或者表述多个字符,这就赋予它能够按照某种模式匹配一系列有相似特征的字符串。一种只能匹配一个字符串的正则表达式是毫无意义的。
简单的说,正则表达式是用一种描述性的语言(其实也是字符串)来定义一种规则,这个规则用于匹配字符串。
python标准模块re,用于支持正则表达式。
下表介绍了常见的正则表达式符号和特殊字符:
表示法 | 描述 | 正则表达式示例 | 匹配的字符串示例 |
符号 | |||
literal | 完全匹配字符串的字面值literal | python | 只能匹配python |
re1|re2 | 匹配正则表达式re1或者re2 | python|foo | python或者foo才能匹配 |
. | 匹配任意字符,除了\n除外 | b.b | bcb |
^ | 匹配字符串起始部分 | ^dear | |
$ | 匹配字符串终止部分 | /bin$ | |
* | 匹配任意次数(包括0次)前面出现的正则表达式 | [A-Z0-9]* | |
+ | 匹配1次或者多次前面出现的正则表达式 | [a-z]+\.com | |
? | 匹配0次或1次前面出现的正则表达式 | goo? | |
{N} | 匹配N次前面出现的正则表达式 | [0-9]{3} | |
{M,N} | 匹配M到N次前面出现的正则表达式 | [0-9]{4,6} | |
[…] | 匹配字符集中的任意单一字符 | [abc] | |
[..x-y..] | 匹配x-y范围内中的任意单一字符 | [0-9],[A-Za-z0-9] | |
[^...] | 不匹配字符集中的任意一个字符 | [^A-Z] | |
(*|+|?{})? | 匹配上面重复出现符号的非贪婪版本(*、+、?、{}) | .*?[a-z] | () |
(...) | 匹配封闭的正则表达式,并另存为子组 | ([0-9]{3})?,f(oo|u)bar | |
特殊字符 | |||
\d | 匹配任意一个数字,和[0-9]一致,\D匹配效果相反 | ||
\w | 匹配任意的数字或者字母,与[A-Aa-z0-9]相同,\W匹配效果相反 | ||
\s | 匹配任何空字符,与[\n\t\r\v\f]相同,\S匹配效果相反 | of\sthe | |
\b | 匹配任何单词边界,\B匹配效果相反 | \bThe\b | |
\N | 匹配已保存的子组N | ||
\c | 逐字匹配特殊字符c(即不转义的匹配) | \.,\\,\* | |
\A(\Z) | 匹配字符串的开始(结束) | \Adear | |
扩展方法 | |||
(?iLmsux) | 在正则表达式中嵌入一个或多个特殊标记参数 | (?x),(? im) | |
(?:...) | 表示一个匹配不用保存的分组 | ||
(?P<name>...) | 像一个仅由name标识而不是数字ID标识的正则分组匹配 | ||
(?P=name) | |||
(?#...) | 注释 | ||
(?=...) | 正向前视断言:如果...出现在之后的位置,而不使用输入字符串 | ||
(?!...) | 负向前视断言:如果...不出现在之后的位置,而不使用输入字符串 | ||
(?<=...) | 正向后视断言:如果...出现在之前的位置,而不使用输入字符串 | ||
(?<!...) | 负向后视断言:如果...不出现在之前的位置,而不使用输入字符串 | ||
(?(id/name)Y|N) | 如果分组所提供的id和name存在,就返回正则表达式的条件匹配Y,如不存在,返回N,其中|N是可选项 |
下面我们分别介绍
使用择一匹配符
| (竖线)表示择一匹配。表示从多个模式中选择其中一个。用于分割不同的正则表达式
正则表达式模式 | 匹配的字符串 |
at|home | at 、 home |
haha|hehe | haha 、 hehe |
匹配任意单一字符
. (点号)可以匹配除了\n(换行符)之外的任意字符。(不过,python正则表达式有一个编译标记[S或者DOTALL],可以推翻这个限制,使得点号能够匹配换行符)
正则表达式 | 匹配字符串 |
f.o | 匹配在字母f和o之间的任意一个字符:fao、f9o等 |
.. | 任意两个字符 |
.end | 匹配在字符串end之前的任意一个字符 |
如何匹配句点(.)或者句号(。)呢?使用转义标识反斜线,如‘\.’
从字符串开始或者结束或者单词边界匹配
如果要匹配字符串的开始配置,必须使用脱字符(^),匹配字符串末尾,需使用$。
我们这里要强调一下:一般提到匹配的时候,是指在字符串的开始位置进行匹配,而提到搜索的时候,是指试图从字符串任何位置开始匹配。下面是一些表示‘边界绑定’的正则表达式搜索模式:
正则表达式 | 匹配的字符串 |
^From | 任何以From作为起始的字符串 |
/bin/sh$ | 任何以/bin/sh结尾的字符串 |
^Subject:hi$ | 任何由单独字符串Subject:hi构成的字符串 |
特殊字符\b和\B可以用来匹配字符边界。区别:\b用于匹配一个单词的边界,意味着如果一个模式必须位于单词的起始部分(单词位于字符串中间),就不管该单词前面是否有任何字符(单词位于行首)。同样的,\B匹配出现在一个单词中间的模式(即,不是单词边界):
正则表达式 | 匹配的字符串 |
the | 任何包含the的字符串 |
\bthe | 任何以the开始的字符串 |
\bthe\b | 仅仅匹配the |
\Bthe | 任何包含the但是并不以the作为开始的字符串 |
创建字符集
句点(.)可以匹配任何字符,但是如果我们想匹配某些特定字符呢?这时可以使用方括号,能够匹配一对方括号中的任何字符:
正则表达式 | 匹配的字符串 |
b[cde]f | bcf、bdf、bef |
[cr][23][dp] | 一个包含三个字符的字符串,第一个字符可以是c、r中的一个,然后是2或者3,最后是d或者p。 |
限定范围和否定
方括号中两个字符中间用连字符(-)连接的话,用于指定一个字符的范围。如果,方括号内第一个字符是脱字符(^),表示不匹配给定字符集中的任何一个字符:
正则表达式 | 匹配的字符串 |
z.[0-9] | 字母z之后跟着任何一个字符,最后跟着一个数字 |
[r-u][env-y][us] | 第一个字符是r到u中间的任一个字母,然后跟着e、n、v、w、x或者y,最后跟着是u或者s |
[^aeiou] | 任意一个非元音字符 |
[^\t\n] | 不匹配制表符或者\n |
[''-a] | 在ASCII系统中,所有字符都位于“”和“”“a”之间,即34-97之间 |
使用闭包操作符实现存在性和频数匹配
主要介绍特殊符号*、+、?、{},主要用于匹配一个、多个或者没有出现的字符串模式。星号*将匹配其左边出现的正则表达式出现0次或者多次出现的情况(计算机编程语言和编译原理中,该操作称为Kleene闭包)。加号+匹配一次或多次出现的正则表达式(也叫作正闭包操作符),问号?匹配0次或者一次出现的正则表达式,{N}匹配前面出现的正则表达式N次,{N-M}将匹配N到M次:
正则表达式 | 匹配的字符串 |
[dn]ot? | 字母d或者n,后面跟着一个o,然后是最多一个t。例如do、no、dot、not |
0?[1-9] | 任何数值数字,他前面可能有一个0. |
[0-9]{15,16} | 匹配15或者16个数字 |
</?[^>]+> | 匹配全部有效的(和无效的)HTML标签 |
[KQRBNP][a-h][1-8]-[a-h][1-8] | '长代数'标记法中,表示国际象棋合法的棋盘移动(仅移动)。即K、Q、R、B、N或者P等字母后面加'a1'~'h8'之间的棋盘坐标。 |
表示字符集的特殊字符
可以用一些缩写来表示字符集:
正则表达式 | 匹配的字符串 |
\w+-\d+ | 一个由字母数字组成的字符串和一串由一个连字符分割的数字 |
[A-Za-z]\w* | 第一个字符是字母,其余字符(如果存在的话)可以是数字或者字母 |
\d{3}-\d{3}-\d{4} | 美国电话号码的格式,前面是区号前缀,如 800-555-1212 |
\w+@\w+\.com | 以XXX@XXX.com命名格式表示的简单电子邮件地址 |
使用小括号指定分组
有时候,我们不仅想要知道整个字符串是否匹配,而且想知道能否提取任何之前已经成功匹配的特定字符串或者子字符串。这时候,我们可以使用小括号()包裹任何正则表达式即可。小括号可以实现两个功能:
- 对正则表达式进行分组
- 匹配子组
为何要对正则表达式进行分组呢?使用场景有:当有两个不同的正则表达式而且我们想用他们来比较同一个字符串时;对正则表达式进行分组可以在整个正则表达式中使用重复操作符。
正则表达式 | 匹配的字符串 |
\d+(\.\d*)? | 表示简单浮点数的字符串。任何十进制数字,后面可以接一个小数点和0个或者多个十进制数字,例如0.001,2,70. |
(Mr?s?\.)?[A-Z][a-z]*[A-Za-z]+ | 名字和姓氏,以及对名字的限制(如果有,首字母必须大写,后续字母小写),全名前可以有可选的Mrs、Mr、Ms.或者M.作为称谓。以及灵活可选的姓氏,可以有多个单词、横线以及大写字母。 |
扩展表示法
以(?...)表示的是扩展表示法(尽管使用了小括号,但是只有(?P<name>)表示一个分组匹配):
正则表达式 | 匹配的字符串 |
(?:\w+\.)* | 以句点作为结尾的字符串,如google.、Facebook.,但是这些匹配不会保存下来供后续的使用和检索 |
(?#comment) | 注释 |
(?=.com) | 如果一个字符串后面跟着.com,才做匹配,并不使用任何目标字符串 |
(?!.net) | 如果一个字符串后面不是跟着.net,才做匹配 |
(?<=800-) | 如果一个字符串的前面跟着800-,才做匹配,并不使用任何输入字符串 |
(?<!192\.168\.) | 如果一个字符串之前不是192.168.,才做匹配 |
(?(1)y|x) | 如果一个匹配组1(\1)存在,就与y匹配,否则,于x匹配 |
重点来了:python如何通过使用re模块来支持正则表达式。详见下节内容!