点号的默认边界
点号匹配任意一个字符,但多数引擎默认把换行排除在外,也就是它匹配除了换行符以外的任意字符。想让点号连换行一起吃掉,必须打开单行模式(又称点号匹配换行模式)。点号真正的边界从来不是「任何东西」,而是「当前模式下允许的任意一个字符」。
点号也是元字符
点号是元字符里最容易被忽略的一个:写在字符类内部时它退化成普通字符,所以要匹配字面点号,写反斜杠加点或用只含点号的字符类都行,后者在 shell 与多层转义场景里更省心。
基本信息
| 默认行为 | 匹配除换行符之外的任意单个字符 |
|---|---|
| 跨行开关 | PCRE/Java/JS/Python 的单行模式,Python 里是 re.DOTALL,JS 里是 s 标志 |
| 是否匹配回车 | PCRE 默认只排除换行,回车符仍会被匹配,文本混用换行风格时会踩坑 |
| 类内行为 | 写在方括号里就是普通字符,不再具有通配含义 |
| 性能提示 | 连续两个点号等于两次无约束尝试,写多了会显著增加回退次数 |
典型使用场景
- 匹配固定长度的编码、流水号:用点号占位比写字符类直观
- 提取日志中两个分隔符之间的内容,此时通常要配非贪婪量词
- 跨行提取文本块,必须显式打开单行模式,否则匹配会在第一行末尾中断
- 写「任意字符但排除某几个」的规则时,字符类比点号更精确
- 排查换行相关怪问题:确认目标串究竟用的是换行、回车换行还是回车
跨行与不跨行对比
跨行与不跨行对比
import re
t = 'start\nend'
re.search(r'start.end', t) # 默认不跨行,这里匹配失败
re.search(r'start.end', t, re.DOTALL) # 打开单行模式后匹配成功
re.search(r'(?s)start.end', t) # 用内联修饰符写法等效
re.findall(r'start.*?end', t, re.DOTALL) # 配合非贪婪,避免吃过头
re.search(r'start[\s\S]*end', t) # 不依赖模式开关的等价写法跨行开关对照
| 引擎 | 开关写法 | 默认点号是否含换行 |
|---|---|---|
| Python re | re.DOTALL 或 (?s) | 否 |
| JavaScript | /模式/s 标志 | 否 |
| Java Pattern | Pattern.DOTALL 或 (?s) | 否 |
| .NET | RegexOptions.Singleline 或 (?s) | 否 |
| PCRE2 | PCRE2_DOTALL 或 (?s) | 否 |
| POSIX grep/sed | 无此开关 | 否,且无等价通用开关 |
跨行有两条路线:打开单行模式让点号吃换行,或干脆不用点号而写「非空白字符」类加上空白字符类的并集。后者不依赖开关,在配置文件、跨工具复用场景里更稳。
点号与贪婪量词组合是性能陷阱的高发区:像点号加星号这样紧跟在两个锚点之间,几乎注定要大量回退,能换成精确字符类就不要用点号。