速查 05 · 正则
按要匹配什么索引。可直接复制使用。 在线验证推荐:regex101.com(选 JavaScript 或 Python 模式)。
目录
基础语法
字符
| 表达式 | 含义 |
|---|---|
. | 任意字符(不含换行) |
\d | 数字 [0-9] |
\D | 非数字 |
\w | 单词字符 [A-Za-z0-9_] |
\W | 非单词字符 |
\s | 空白(空格/制表/换行) |
\S | 非空白 |
[abc] | a、b、c 中任意一个 |
[^abc] | 除 a、b、c 外任意 |
[a-z] | 范围 |
数量
| 表达式 | 含义 |
|---|---|
* | 0 次或多次 |
+ | 1 次或多次 |
? | 0 或 1 次 |
{n} | 恰好 n 次 |
{n,} | 至少 n 次 |
{n,m} | n 到 m 次 |
*? +? | 惰性(尽可能少匹配) |
位置与分组
| 表达式 | 含义 |
|---|---|
^ | 行/串开头 |
$ | 行/串结尾 |
\b | 单词边界 |
(...) | 捕获分组 |
(?:...) | 非捕获分组 |
(?<name>...) | 命名分组 |
\1 $1 | 反向引用第 1 组 |
(?=...) | 正向预查(后面是…) |
(?!...) | 负向预查(后面不是…) |
常用校验
regex
# 手机号(中国大陆)
^1[3-9]\d{9}$
# 邮箱
^[\w.+-]+@[\w-]+(\.[\w-]+)+$
# 身份证(18 位,简化校验)
^\d{17}[\dXx]$
# 强密码:8-20 位,含大小写字母和数字
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[A-Za-z\d]{8,20}$
# URL
https?://[\w.-]+(:\d+)?(/[\w./?%&=~-]*)?
# IP 地址(v4)
^((25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$
# 日期 yyyy-MM-dd
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
# 时间 HH:mm:ss
^([01]\d|2[0-3]):[0-5]\d:[0-5]\d$
# 中文字符
[\u4e00-\u9fa5]
# 纯中文(至少一个)
^[\u4e00-\u9fa5]+$
# 数字(含小数)
^-?\d+(\.\d+)?$
# 正整数
^[1-9]\d*$
# 十六进制颜色
^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$
# 车牌号(含新能源)
^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4,5}[A-Z0-9挂学警港澳]{1}$提取与替换
常见提取
regex
# 提取括号内容
(?<=\()[^)]+(?=\))
# 提取引号内容
"([^"]*)"
# 提取 HTML 标签内容(简单场景,复杂 HTML 请用解析器)
<(\w+)>(.*?)</\1>
# 提取 URL 参数值
[?&]key=([^&]*)
# 提取 Markdown 链接
\[([^\]]+)\]\(([^)]+)\)
# 提取数字
-?\d+\.?\d*
# 去除首尾空白
^\s+|\s+$
# 匹配重复行(去重时用)
^(.*)\n\1$批量替换示例(编辑器 / sed)
| 目标 | 查找 | 替换 |
|---|---|---|
日期格式 2026/09/03 → 2026-09-03 | (\d{4})/(\d{2})/(\d{2}) | $1-$2-$3 |
驼峰转下划线 userName → user_name | ([a-z])([A-Z]) | $1_$2(再转小写) |
| 空行删除 | ^\s*\n | (空) |
| 行尾空格删除 | [ \t]+$ | (空) |
| 单引号改双引号 | '([^']*)' | "$1" |
在代码中用
Dart
dart
final reg = RegExp(r'^1[3-9]\d{9}$'); // r 前缀:原始字符串,不用双重转义
reg.hasMatch('13800138000'); // → true
// 提取所有匹配
final matches = RegExp(r'\d+').allMatches('a1b22c333');
matches.map((m) => m.group(0)); // → (1, 22, 333)
// 提取分组
final m = RegExp(r'(\d{4})-(\d{2})').firstMatch('2026-09');
m?.group(1); // → '2026'
// 替换
'2026/09/03'.replaceAllMapped(
RegExp(r'(\d{4})/(\d{2})/(\d{2})'),
(m) => '${m[1]}-${m[2]}-${m[3]}',
); // → '2026-09-03'JavaScript / TypeScript
js
/^1[3-9]\d{9}$/.test('13800138000'); // → true
'2026/09/03'.replace(/(\d{4})\/(\d{2})\/(\d{2})/, '$1-$2-$3');
'a1b22'.match(/\d+/g); // → ['1', '22']
// 全局匹配务必注意 lastIndex(用 match/all 而非 exec 循环)
[...'a1b22'.matchAll(/\d/g)].map(m => m[0]);Python
python
import re
re.match(r'^1[3-9]\d{9}$', '13800138000')
re.findall(r'\d+', 'a1b22c333') # → ['1', '22', '333']
re.sub(r'(\d{4})/(\d{2})/(\d{2})', r'\1-\2-\3', '2026/09/03')易错点
| 坑 | 说明 | 解法 |
|---|---|---|
| 忘记转义 | . * + ? ( [ 等都是元字符 | 匹配字面量用 \. \* 等 |
| 正则里反斜杠被吃掉 | 字符串先转义一次,正则再转义一次 | 用原始字符串:Dart 的 r''、Python 的 r''、JS 的 /.../ |
| 贪婪匹配吃掉太多 | .* 会尽可能多匹配 | 改用惰性 .*?,或用否定类 [^"]* |
| 校验没加首尾锚点 | \d{11} 会匹配更长串的一部分 | 校验必须加 ^...$ |
| 用正则解析 HTML/JSON | 嵌套结构正则无法可靠处理 | 用专门的解析器(html 包 / jsonDecode) |
| 中文范围不对 | [\u4e00-\u9fa5] 只覆盖基本区 | 扩展区需补 [\u3400-\u4DBF] 等 |
| 灾难性回溯 | (a+)+ 类嵌套量词在长串下卡死 | 避免嵌套量词;或限制输入长度 |
性能提醒
复杂正则在长文本上可能极慢(回溯爆炸)。两条实践规则:
- 优先用否定字符类
[^"]*而非.*?(减少回溯) - 避免嵌套量词
(a+)+、(\d+)*这类写法