标志位概述
标志位(Flags)是修改正则引擎默认匹配行为的开关。不同语言的标志位语法不同,但含义基本一致。
标志位(Flag)
正则表达式的修饰符,改变引擎的默认行为(如大小写敏感性、多行匹配、点号是否匹配换行等)。Python 中以常量形式传入,可用
| 组合多个标志;JavaScript 中在正则字面量末尾追加字母(如 /re/gim)。| 含义 | Python | JavaScript | PCRE |
|---|---|---|---|
| 忽略大小写 | re.IGNORECASE / re.I | i | (?i) |
| 全局匹配 | (用 findall/finditer) | g | — |
| 多行模式 | re.MULTILINE / re.M | m | (?m) |
| 点号全匹配(DOTALL) | re.DOTALL / re.S | s | (?s) |
| 详细模式(注释) | re.VERBOSE / re.X | — | (?x) |
| ASCII 模式 | re.ASCII / re.A | — | — |
| Unicode 模式 | re.UNICODE / re.U | u | — |
| 粘滞匹配 | — | y | — |
| dotAll(JS) | — | s | — |
| 字符类 v 模式(JS) | — | v | — |
忽略大小写(i)
让整个正则匹配时不区分大小写。这是最常用的标志位之一。
# Python re.findall(r'python', 'Python PYTHON python', re.IGNORECASE) # → ['Python', 'PYTHON', 'python'] # 在 compile 中指定 KEYWORD = re.compile(r'python', re.I) KEYWORD.findall('I love Python and PYTHON') # → ['Python', 'PYTHON'] // JavaScript 'Hello WORLD'.match(/hello world/i) // → ['Hello WORLD'] 'Hello WORLD hello'.match(/hello/gi) // → ['Hello', 'hello']
INFO(i 标志与字符类)
re.IGNORECASE 会影响字符类中的字母范围。[a-z] 加上 re.I 后等价于 [a-zA-Z]。但字符类中的 ^(取反)同样受影响——[^a-z] 加 re.I 会排除所有大小写字母。
多行模式(m)
默认情况下,^ 和 $ 匹配整个字符串的开头和结尾。开启 MULTILINE 后,它们匹配每一行的开头和结尾。
INFO(多行模式的精确含义)多行模式改变的是
^ 和 $ 锚点的语义,而不是让 . 匹配换行符(那是 DOTALL 的功能)。这两个功能相互独立,可以同时启用。
text = """第一行数据 第二行数据 第三行数据""" # 默认:^ 只匹配整个字符串的开头 re.findall(r'^\S+', text) # → ['第一行数据'] (只有第一行) # 多行模式:^ 匹配每行的开头 re.findall(r'^\S+', text, re.MULTILINE) # → ['第一行数据', '第二行数据', '第三行数据'] # $ 同理 re.findall(r'\S+$', text, re.MULTILINE) # → ['第一行数据', '第二行数据', '第三行数据']
/^\d+\./gm(多行)
1. 苹果
2. 香蕉
3. 橙子
2. 香蕉
3. 橙子
多行模式下 ^ 匹配每行行首,提取每行的序号
# 实用示例:提取日志文件中每行的时间戳 log = """2026-03-26 10:00:01 INFO Started 2026-03-26 10:00:05 ERROR Failed 2026-03-26 10:00:10 INFO Done""" re.findall(r'^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}', log, re.MULTILINE) # → ['2026-03-26 10:00:01', '2026-03-26 10:00:05', '2026-03-26 10:00:10'] # 替换每行的前缀 re.sub(r'^', '> ', log, flags=re.MULTILINE) # 在每行开头添加 '> '
DOTALL 模式(s)
让 . 匹配包括换行符在内的任意字符,适合处理多行内容。
# 默认:. 不匹配换行 re.search(r'start.*end', 'start\nend') # → None(. 无法跨越 \n) # DOTALL:. 也匹配换行 re.search(r'start.*end', 'start\nend', re.DOTALL) # → 匹配 'start\nend' # 提取跨行的 HTML 注释 html = """<!-- This is a multi-line comment -->""" re.findall(r'<!--.*?-->', html, re.DOTALL) # → ['<!--\n This is a\n multi-line comment\n-->'] # 不使用 re.DOTALL 的替代方案:[\s\S]* 匹配任意字符(包括换行) re.findall(r'<!--[\s\S]*?-->', html) # 等效结果(在不支持 DOTALL 的环境中用)
详细模式(x / re.VERBOSE)
详细模式让你可以在正则表达式中添加空格和注释(# 开头),大幅提升复杂正则的可读性。
INFO(详细模式规则)开启
re.VERBOSE 后:① 空格(除非在字符类中或用 \ 转义)会被忽略;② # 到行尾的内容视为注释;③ 字符类 [...] 内部的空格不受影响。如需匹配字面空格,用 \ (反斜杠+空格)或 \s。
# 不使用详细模式(可读性差) EMAIL = re.compile(r'^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$') # 使用详细模式(清晰的注释) EMAIL = re.compile(r""" ^ # 字符串开头 [a-zA-Z0-9._%+\-]+ # 用户名:字母、数字、._%+- @ # @ 符号 [a-zA-Z0-9.\-]+ # 域名:字母、数字、点、连字符 \. # 字面点号 [a-zA-Z]{2,} # 顶级域名:2位或更多字母 $ # 字符串结尾 """, re.VERBOSE) # 详细模式中的空格和注释 re.search(r""" \d+ # 数字 [\s,]+ # 空白或逗号 \d+ # 更多数字 """, '123, 456', re.VERBOSE) # 匹配 '123, 456'
组合多个标志
# Python:用 | 组合多个标志 re.compile(r'pattern', re.IGNORECASE | re.MULTILINE | re.DOTALL) # 或使用简写 re.compile(r'pattern', re.I | re.M | re.S) # 同时使用详细模式和其他标志 PATTERN = re.compile(r""" (?P<year>\d{4}) # 年 - (?P<month>\d{2}) # 月 - (?P<day>\d{2}) # 日 """, re.VERBOSE | re.ASCII) // JavaScript:在末尾叠加多个标志字母 /pattern/gims // 全局+忽略大小写+多行+DOTALL
内联标志(Inline Flags)
内联标志嵌入在正则内部,只影响该位置之后(或括号内)的部分:
# 影响整个模式(放在开头) re.search(r'(?i)hello', 'HELLO') # 匹配 # 只影响括号内 re.findall(r'(?i:hello) world', 'HELLO world') # 匹配 re.findall(r'(?i:hello) world', 'HELLO WORLD') # None(world 区分大小写) # 多个内联标志组合 re.search(r'(?im)^python', 'PYTHON\ncode') # i(忽略大小写)+ m(多行) # 取消标志(Python 支持) re.search(r'(?i)hello(?-i)world', 'HELLOWORLD') # None:hello 不区分大小写,world 区分大小写 re.search(r'(?i)hello(?-i)world', 'HELLOworld') # 匹配 'HELLOworld'
JavaScript 特有标志
y 标志(粘滞匹配)
const re = /\d+/y re.lastIndex = 0 '123abc456'.match(re) // → ['123'],lastIndex 自动更新为 3 re.lastIndex // → 3 // 下一次从 lastIndex=3 开始,且必须从该位置精确匹配 '123abc456'.match(re) // → null(位置3是 'a',不是数字) // 对比 g 标志:g 标志会跳过不匹配的位置继续搜索 '123abc456'.match(/\d+/g) // → ['123', '456'](跳过 abc)
v 标志(ES2024,扩展字符类)
// v 标志启用新的字符类特性(不能与 u 同时使用) /[\p{Decimal_Number}--[0-9]]/v // 差集:Unicode 数字减去 ASCII 数字 /[\p{ASCII}&&\p{Letter}]/v // 交集:ASCII 且是字母
re.ASCII vs re.UNICODE
Python 3 中正则默认以 Unicode 模式运行,\w、\d、\s 匹配 Unicode 范围的字符。加 re.ASCII 可以限制为仅 ASCII 范围:
re.UNICODE(默认)
Python 3 的默认行为。
\w 匹配任何 Unicode 字母/数字/下划线(包括中文、日文、韩文等);\d 匹配任何 Unicode 数字(包括全角数字 0-9);\s 匹配任何 Unicode 空白字符。re.ASCII(re.A)
将
\w、\d、\s 及其大写反义限制为只匹配 ASCII 范围([a-zA-Z0-9_]、[0-9]、[ \t\n\r\f\v])。适合只处理英文文本或需要严格 ASCII 兼容的场景。# 默认(Unicode 模式) re.findall(r'\w+', 'hello 你好 123') # → ['hello', '你好', '123'] (汉字也是 \w) # ASCII 模式 re.findall(r'\w+', 'hello 你好 123', re.ASCII) # → ['hello', '123'] (只匹配 ASCII 单词字符) # 对 \d 的影响 re.findall(r'\d+', 'price: 260元') # → ['260'](全角数字) re.findall(r'\d+', 'price: 260元', re.ASCII) # → [](只有半角数字才算)
WARNING(MULTILINE 与 DOTALL 的常见混淆)多行模式(
re.MULTILINE/m)和 DOTALL(re.DOTALL/s)控制的是不同的行为,经常被混淆:MULTILINE 只改变 ^ 和 $ 的锚点含义;DOTALL 只改变 . 是否匹配换行符。两者可以独立或组合使用。如果你想"匹配跨行的任意内容",需要的是 DOTALL,而不是 MULTILINE。
标志位的实际应用场景
| 应用场景 | 推荐标志 | 原因 |
|---|---|---|
| 关键词搜索(不区分大小写) | re.I | 用户输入可能大小写混用 |
| 提取每行行首标记 | re.M | ^ 需要匹配每行开头 |
| 提取多行 HTML/JSON 内容 | re.S | . 需要跨越换行 |
| 写复杂的生产级正则 | re.X | 可读性和注释 |
| 处理纯 ASCII 表单验证 | re.A | 排除全角字符干扰 |
| 日志分析(同时多行+忽略大小写) | re.I | re.M | 组合使用 |
# 综合示例:解析多行配置文件 config = """ # 数据库配置 HOST = localhost PORT = 5432 DATABASE = mydb # 生产数据库 """ # 详细模式 + ASCII 模式:提取所有键值对,忽略注释 KEY_VALUE = re.compile(r""" ^ # 行首(需要 re.M) (?!\#) # 不是注释行(负向前瞻) (\w+) # 键名 \s*=\s* # 等号(两边允许空白) ([^#\n]+?) # 值(到 # 或换行前,非贪婪) \s*$ # 到行尾(允许尾随空白) """, re.VERBOSE | re.MULTILINE | re.ASCII) KEY_VALUE.findall(config) # → [('HOST', 'localhost'), ('PORT', '5432'), ('DATABASE', 'mydb')]
小结
本章要点
i(IGNORECASE):大小写不敏感;Python 用re.I,JS 在末尾加im(MULTILINE):让^和$匹配每行首尾——不改变.的行为s(DOTALL):让.也匹配换行符;不用 DOTALL 时可用[\s\S]代替x(VERBOSE):允许空格和#注释,是写复杂正则的必备工具- Python 3 默认 Unicode 模式;
re.ASCII限制\w \d \s只匹配 ASCII - Python 用
|组合多个标志;JavaScript 在末尾叠加字母 - 内联标志
(?i)、(?m)等可以嵌入正则内部,精确控制作用范围 - MULTILINE 与 DOTALL 互相独立,常见误用是把两者搞混