正则表达式详解
正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串,通常用于字符串的查询和替换。许多程序设计语言都支持利用正则表达式进行字符串操作(例如perl、javascript)。正则表达式的历史较为悠久,最初出现在理论计算机科学的自动控制理论和形式化语言理论中。
正则表达式目前使用较为广泛的PCRE规范,适用于perl和python语言,当然其它语言也通过各种形式的库提供了相关支持。在正则表达式中最为核心的概念是模式,而模式本身就是由规定的各种结构组成的字符串。正则表达式主要结构有普通字符、特殊字符、限定符、定位符、选择。正则表达式语法
正则表达式中模式是由普通字符(例如字符a到z)以及特殊字符(称为"元字符")组成。模式描述在搜索文本时要匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。正则表达式的语法本质上是模式的基础结构间的运算规则。对于正则表达式背后深刻的数学理论,不建议做过多的深入了解,但是作为工程师,需要做到得心应手并且常用的一些模式(例如邮箱、手机号、银行卡等)需要信手拈来。
普通字符
普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。常见的不可打印字符如下表:
字符 | 描述 |
---|---|
\n | 换行符 |
\r | 回车符 |
\f | 换页符 |
\t | 制表符 |
\v | 垂直制表符 |
\s | 任何空白字符,包括空格、制表符、换页符等等 |
\cX | 当X是处于A到Z之间的字符的时候,匹配字符串中的一个控制符,例如,\cM 匹配字符串中的 control-M (U+000D) |
特殊字符
特殊字符,主要指的是一些用于正则表达式模式构成中的一些符号,类似于编程语言中的保留字。如果待匹配的字符串中包含有特殊字符,通过在特殊字符前添加转义符号就可以了。正则表达式中的特殊字符如下表:
特别字符 | 描述 |
---|---|
$ | 匹配输入字符串的结尾位置。要匹配 $ 字符本身,需使用 \$ |
( ) | 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,需使用 \( 和 \) |
* | 匹配前面的子表达式零次或多次。要匹配星号字符,需使用 \* |
+ | 匹配前面的子表达式一次或多次。要匹配 + 字符,需使用 \+ |
. | 匹配除换行符 \n 之外的任何单字符。要匹配 . ,需使用 \. |
[ ] | 标记一个中括号表达式的开始和结束。要匹配 [ 或 ] ,请使用 \[ 和 \] |
? | 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,需使用 ? |
\ | 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。要匹配该符号,需使用 \\ |
^ | 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,需使用 \^ |
{ } | 标记限定符表达式的开始和结束。要匹配 { 和 },需使用 \{ 和 \} |
| | 指明两项之间的一个选择。要匹配 |,需使用 \| |
\d | 匹配一个数字,等价于[0-9] |
\D | 匹配一个非数字字符,等价于[^0-9] |
\S | 匹配一个非空白字符 |
\w | 匹配一个单字字符(字母、数字或者下划线,等价于[A-Za-z0-9_] |
\W | 匹配一个非单字字符,等价于[^A-Za-z0-9_] |
\n | 返回最后的第n个子捕获匹配的子字符串 |
\0 | 匹配 NULL (U+0000) 字符 |
限定符
限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 * 、 + 、 ? 、 {n} 、 {n,} 和 {n,m} 共6种形式。限定符就相当于中文所说的量词。限定符的6种形式的含义如下表:
字符 | 描述 |
---|---|
* | 匹配前面的子表达式零次或多次,等价于{0,} |
+ | 匹配前面的子表达式一次或多次,等价于 {1,} |
? | 匹配前面的子表达式零次或一次,等价于 {0,1} |
{n} | n 是一个非负整数,匹配前面的子表达式 n 次 |
{n,} | n 是一个非负整数,至少匹配前面的子表达式 n 次 |
{n,m} | m 和 n 均为非负整数,其中n <= m,表示最少匹配 n 次且最多匹配 m 次,需要注意在逗号和两个数之间不能有空格 |
正则表达式中对于限定符数量的确定,默认使用的是贪婪模式,即匹配到最大数量,而非贪婪模式指的是匹配最小数量。想要使用非贪婪模式,只需要在限定符后添加 ? 符号即可。例如, 模式 ^do\.+?es$ 在字符串 does doaes doaaes中,会匹配does。
定位符
定位符用于描述模式的边界,例如字符串的开始位置、结束位置、单词的边界位置等,正则表达式中定位符有** ^、 $ 、 \b 、 \B**。定位符的相关含义如下表:
字符 | 描述 |
---|---|
^ | 匹配输入字符串开始的位置 |
$ | 匹配输入字符串结尾的位置 |
\b | 匹配一个字边界,即字与空格间的位置 |
\B | 非字边界匹配 |
选择
正则表达式中的圆括号主要作用为选择和捕获。用圆括号将所有选择项括起来,相邻的选择项之间用 | 分隔,可以实现选择效果。但用圆括号会有一个副作用,使相关的匹配会被缓存,此时可用 ?: 放在第一个选项前来消除这种副作用。其中 ?: 是非捕获元之一,还有两个非捕获元是 ?= 和 ?!,这两个还有更多的含义,前者为肯定预查,在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串,后者为否定预查,在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串。对于这两句话,用符号解释起来将会更加形象。参考如下:
1=> A(?=B); 2=> A(?!B); 3=> (?<=B)A; 4=> (?<!B)A
- 正向肯定预查,查找A,且A后面要有B,结果为A
- 正向否定预查,查找A,且A后面不能有B, 结果为A
- 反向肯定预查,查找A,且A前面要有B,结果为A
- 反向否定预查,查找A,且A前面不能有B,结果为A
示例如下:
# !/usr/bin/env python3# -*- coding: utf-8 -*-import restr1 = "Windows2000"str2 = "Windows3.1"pattern1 = R"^Windows(?=95|98|NT|2000)"res11 = re.match(pattern1, str1)if res11: print(res11.group())else: print("没有匹配项")res12 = re.match(pattern1, str2)if res12: print(res12.group())else: print("没有匹配项")patter2 = R"^Windows(?!95|98|NT|2000)"res21 = re.match(patter2, str1)if res21: print(res21.group())else: print("没有匹配项")res22 = re.match(patter2, str2)if res22: print(res22.group())else: print("没有匹配项")
上面这段代码的运行输入结果如下:
正则表达式中的正向预查和反向预查,主要用于限制目标结果的周边不应包含什么或者包含什么,更多的是对结果的上下文限制。对于正则表达式中的预查具体是如何实现的,感兴趣的同学可以深入了解,个人认为一般情况下,大家记住正向预查和反向预查主要用于什么情景下即可。常见正则表达式
作用 | 模式 |
---|---|
3-16个英文字符组成的用户名 | ^[a-z0-9_-]{3,16}$ |
密码 | ^[a-z0-9_-]{6,18}$ |
十六进制值 | ^#?([a-f0-9]{6}|[a-f0-9]{3})$ |
Unicode编码中的汉字范围 | ^[\u2E80-\u9FFF]+$ |
该表格后续会继续扩展。