两类字符
正则由两种成分构成:字面量字符按原样比对,元字符代表某一类操作,比如重复、选择、分组、定位。同一个符号在类外是元字符、在类内往往退化为普通字符,点号就是最典型的例子。写模式时真正要判断的是:这个位置到底按字面比,还是按语法解释。
为什么必须有元字符
如果只有字面量,正则就退化成字符串查找。元字符把「任意一个数字」「出现零次或多次」「两支之一」这类共性抽象出来,代价是它们占用了键盘上的普通符号,想表示符号本身就得转义。
基本信息
| 类外元字符 | 点、^ $ * + ? ( ) [ ] { } | 以及反斜杠本身 |
|---|---|
| 类内仍特殊 | 反斜杠、^(在首位时取反)、-(在中间表示区间)、右中括号需放首位 |
| 转义写法 | 在元字符前加反斜杠,如 \. \* \[ 双反斜杠表示单个反斜杠 |
| 语义扩展 | 反斜杠还能把普通字母变成特殊序列,如 \d \w \b \n |
| 引擎差异 | POSIX 基本正则(grep、sed 默认)里 + ? { } 需转义才生效;Python 3.7 起对未知转义直接报错 |
典型使用场景
- 匹配 IP、域名、文件名里的点号:只能用转义写法,否则会连任意字符一起吞掉
- 匹配价格里的美元符号、代码里的括号与星号,必须逐个转义
- 在 grep、sed 默认的基本正则下写量词,要补反斜杠才能生效
- 把用户输入拼进模式前,统一对全部元字符做转义,防止输入被当成语法
- 排查模式为什么匹配不上:先确认某个符号是否被引擎当成了元字符
转义与误用对比
转义与误用对比
import re
re.search(r'1.2.3.4', '1x2y3z4') # 点号没转义:能匹配,结果不可信
re.search(r'1\.2\.3\.4', '1.2.3.4') # 转义后才是真正的点号
re.escape('1.2.3.4') # 自动转义,拼用户输入时用它
re.search(r'^\[info\]', '[info] ok') # 行首的方括号必须转义
re.findall(r'a\*b', 'a*b aab') # 星号转义后退化为普通字符元字符含义对照
| 符号 | 类外含义 | 要匹配本身时 |
|---|---|---|
| 点 | 任意单个字符(视模式是否跨行) | 写成反斜杠加点 |
| * | 前一元素重复零次或多次 | 反斜杠加星号 |
| [ ] | 字符类,匹配其中任意一个字符 | 反斜杠加方括号 |
| ( ) | 分组并捕获 | 反斜杠加圆括号 |
| | | 或分支 | 反斜杠加竖线 |
| ^ $ | 行或串的首尾锚点 | 反斜杠加锚点符号 |
POSIX 的两种风格要分清:基本正则(BRE)里 + ? { } ( ) | 都是普通字符,想当语法用必须转义;扩展正则(ERE,grep -E)里则相反。所以同一条命令换到另一个工具里常常突然不工作。
反斜杠只在必要时加。过度转义虽然多数时候无害,但在 Python 3.7 以后、以及开启严格校验的引擎里,对普通字母加反斜杠会直接报错,而不是被忽略。