⭐ 觉得好用?收藏备用,下次直接打开 ✨ 开通会员去广告 · 更多权益即将开放 ☕ 支持作者
80 条语法分 9 类,点语法即复制。标了 方言差异 的是 JavaScript 和 PCRE / Python 行为不一样的地方 —— 搜到的教程大多按 PCRE 写,照抄进 JS 会白试半天。 查到想用的写法,去 正则测试器Regex Pro 拿真实文本验一遍再上线。
分类
字符与字符类 #

除换行符外的任意一个字符。

加 s(dotAll)标志后连换行也匹配。没有 s 标志时,惯用写法是 [\s\S]。

字符与字符类 #

方括号里任意一个字符。

匹配 a 或 b 或 c

字符与字符类 #

不是方括号里那些字符的任意一个字符。

^ 只有紧跟在 [ 后面才表示取反,写在别处就是普通的 ^ 字符。

字符与字符类 #

字符范围。可以连写多段,如 [a-zA-Z0-9]。

字符与字符类 #

一个数字,等价于 [0-9]。

字符与字符类 #

一个非数字字符。

字符与字符类 #

一个单词字符,等价于 [A-Za-z0-9_]。

默认不包含中文 —— 用 \w 去匹配中文用户名一定会漏。中文要用 \p{Script=Han} 或 [\u4e00-\u9fa5]。

字符与字符类 #

一个非单词字符。

字符与字符类 #

一个空白字符:空格、制表、换行、回车、换页、垂直制表。

JS 里 \s 还包含不换行空格 \u00a0 和各种 Unicode 空格,从网页复制来的文本里经常藏着它们。

字符与字符类 #

一个非空白字符。

字符与字符类 #

制表符 / 换行符 / 回车符。

Windows 换行是 \r\n,按行切分时写 /\r?\n/ 才两边都吃得下。

字符与字符类 #

按两位十六进制指定字符,如 \x41 是 A。

字符与字符类 #

按四位十六进制指定 UTF-16 码元,如 \u4e2d 是「中」。

需要 u 或 v 标志 字符与字符类 #

按码点指定字符,可超出 BMP(如 emoji)。

字符与字符类 #

任意字符,包括换行。

没有 s 标志时跨行匹配的标准写法。[\d\D]、[\w\W] 同理。

字符与字符类 #

或:两边任选其一。

优先级最低,abc|def 是「abc 或 def」而不是「ab(c 或 d)ef」。要限定范围必须加括号。

字符与字符类 #

转义:让元字符 . * + ? ( ) [ ] { } ^ $ | \ / 变回普通字符。

JS 不支持,grep / PCRE 支持 字符与字符类 #

POSIX 字符类。

量词 #

前面的元素出现 0 次或多次。

量词 #

前面的元素出现 1 次或多次。

量词 #

前面的元素出现 0 次或 1 次。

量词 #

恰好 n 次。

\d{4} 匹配四位数字

量词 #

至少 n 次,上不封顶。

量词 #

n 到 m 次。

逗号后面不能有空格,{2, 5} 在 JS 里会被当成五个普通字符。

量词 #

惰性(非贪婪)量词:在能匹配的前提下尽量少取。

量词默认贪婪。抓 <a>...</a> 里的内容要写 <a>(.*?)</a>,写成 .* 会一路吃到最后一个 </a>。

JS 不支持,PCRE / Java 支持 量词 #

独占量词:匹配后不回溯,用于避免灾难性回溯。

锚点与边界 #

字符串开头。加 m 标志后变成「每一行的开头」。

锚点与边界 #

字符串结尾。加 m 标志后变成「每一行的结尾」。

JS 的 $ 不像 Python 的 $ 那样容忍结尾多一个换行,处理文件内容时容易差这一个字符。

锚点与边界 #

单词边界:\w 与非 \w 之间的位置。

因为定义来自 \w,所以中文两侧不算边界。\b中文\b 这种写法多半不是你想要的效果。

锚点与边界 #

非单词边界。

\B(?=(\d{3})+(?!\d)) 用来插千分位逗号

JS 不支持,Python / PCRE 支持 锚点与边界 #

字符串真正的开头 / 结尾(不受 m 标志影响)。

JS 不支持(用 y 标志代替) 锚点与边界 #

上一次匹配结束的位置。

分组与引用 #

捕获组:既分组,又把匹配到的内容存起来供引用。

分组与引用 #

非捕获组:只分组,不占用组编号。

只是想用括号限定 | 的范围时用它,组编号不会被打乱,引擎也少做一点记录。

分组与引用 #

命名捕获组,用名字而不是编号取值。

JS 里通过 match.groups.name 取。比数第几个括号可靠得多,加一层括号不会全盘错位。

分组与引用 #

反向引用:要求这里重复出现第 n 组匹配到的内容。

(\w)\1 匹配 aa、bb 这种连续重复字符

分组与引用 #

按名字反向引用命名捕获组。

JS 不支持(标志只能写在正则末尾) 分组与引用 #

内联修饰符:从此处起忽略大小写。

断言 #

先行肯定:右边必须能匹配,但不消耗字符。

\d+(?=元) 匹配「100元」里的 100,不含「元」

断言 #

先行否定:右边必须匹配不上。

\d+(?!元) 匹配后面不是「元」的数字

JS 从 ES2018 起支持(Safari 16.4+) 断言 #

后行肯定:左边必须能匹配,不消耗字符。

JS 从 ES2018 起支持(Safari 16.4+) 断言 #

后行否定:左边必须匹配不上。

断言 #

多个先行断言叠加,用来表达「同时满足若干条件」。

密码强度校验的标准写法:三个断言各查一类字符,最后的 .{8,} 才是真正消耗字符的部分。

修饰符 #

global:找出所有匹配,而不是第一个就停。

带 g 的正则对象有 lastIndex 状态,反复用同一个对象调 test() 会出现「一次真一次假」。要么每次新建,要么用完置 lastIndex = 0。

修饰符 #

ignoreCase:忽略大小写。

修饰符 #

multiline:让 ^ 和 $ 匹配每一行的首尾。

修饰符 #

dotAll:让 . 也能匹配换行符。

修饰符 #

unicode:按码点处理,启用 \p{...} 和 \u{...}。

不加 u 的话,一个 emoji 会被当成两个字符,.{1} 只能吃掉半个,结果是乱码。

ES2024,Node 20+ / Chrome 112+ 修饰符 #

unicodeSets:u 的增强版,字符类支持交集 && 和差集 --。

修饰符 #

sticky:只从 lastIndex 处开始匹配,不往后找。常用于写词法分析器。

修饰符 #

hasIndices:结果里额外带上每个捕获组的起止位置(match.indices)。

替换语法 #

替换串里代表整个匹配到的内容。

替换语法 #

替换串里引用第 n 个捕获组。

把 /(\d+)-(\d+)/ 替换成 $2-$1 即可交换两段

替换语法 #

替换串里引用命名捕获组。

替换语法 #

替换串里表示一个字面的 $ 符号。

替换语法 #

匹配位置之前的全部内容。

替换语法 #

匹配位置之后的全部内容。

需要 u 或 v 标志 Unicode / 中文 #

任意字母(任何语言)。

需要 u 或 v 标志 Unicode / 中文 #

任意数字类字符,含全角数字和其他文字体系的数字。

需要 u 或 v 标志 Unicode / 中文 #

任意标点,含中文全角标点。

需要 u 或 v 标志 Unicode / 中文 #

汉字(含扩展区的生僻字)。

比 [\u4e00-\u9fa5] 全面得多,「𠮷」这类扩展区汉字只有它认。

需要 u 或 v 标志 Unicode / 中文 #

平假名。Katakana 是片假名,Hangul 是谚文。

需要 u 或 v 标志 Unicode / 中文 #

大写 P 表示取反:不是字母的字符。

Unicode / 中文 #

常用汉字区间,不需要 u 标志。

这只是 CJK 统一表意文字基本区的一段,扩展区汉字和部分生僻字不在里面。老代码里最常见,但不是最准的写法。

Unicode / 中文 #

零宽空格 / BOM / 不换行空格。

从网页或 Word 复制来的文本里的隐形字符,肉眼看不出来但会让 trim() 和 === 全部失效。清洗文本时记得一起去掉。

常用模式 #

中国大陆手机号(宽松校验)。

只查「1 开头 + 第二位 3–9 + 共 11 位」。号段一直在增加,任何号段白名单写死都会过时;真要确认号码有效只能发一条短信。

常用模式 #

邮箱(实用级校验)。

邮箱不可能用正则校验干净 —— 符合 RFC 5322 的完整正则有几百个字符,而且仍然判断不了地址是否真的存在。生产环境唯一可靠的办法是发验证邮件,正则只用来挡明显的手滑。

常用模式 #

18 位居民身份证号(格式校验)。

只校验格式。最后一位是按 ISO 7064:1983 MOD 11-2 算出来的校验位,正则算不了 —— 需要真校验请用本站的「身份证解析」工具。

常用模式 #

IPv4 地址(每段 0–255)。

不要图省事写成 \d{1,3}(\.\d{1,3}){3},那样 999.999.999.999 也能过。

常用模式 #

日期 YYYY-MM-DD。

正则管不了「这个月有没有 31 号」,2 月 30 日照样通过。日期合法性要交给 Date 或日期库判断。

常用模式 #

24 小时制时间 HH:MM:SS。

常用模式 #

HTTP/HTTPS 网址(宽松)。

浏览器里直接用 new URL(str) 试着构造并 catch,比任何正则都准。

常用模式 #

十六进制颜色值,三位或六位。

常用模式 #

中国大陆邮政编码(六位,不以 0 开头)。

常用模式 #

统一社会信用代码(18 位)。

字符集刻意排除了 I、O、S、V、Z ——标准里就没有这几个字母,避免和数字 1、0、5、2 混淆。

常用模式 #

千分位插入点,配合 replace 加逗号。

匹配的是「位置」而不是字符,所以直接 replace 成 "," 就行。

常用模式 #

首尾空白,配合 g 标志去掉两端空格。

在 JS 里直接用 String.prototype.trim() 就够了,这条主要用在只能写正则的地方(编辑器查找替换、日志过滤规则)。

常用模式 #

三个以上连续换行,用于把多余空行压成一个。

常用模式 #

强密码:至少 8 位,含大小写字母、数字和符号各一。

常用模式 #

连续重复 3 次以上的同一个字符,用于挡「aaaaaa」这类垃圾输入。

正则语法速查收了 80 条语法,分 9 类,全部带中文释义,点语法即复制。这页解决的是「查语法」,验证表达式请去正则测试器Regex Pro

为什么单独标「方言差异」

正则不是一种语言,是一族方言。JavaScript、PCRE、Python、Go 的 RE2、Java 各有各的取舍,而中文搜索结果里绝大多数正则教程是按 PCRE 写的。差异最常见的几处:

  • 后行断言 (?<=...):JS 到 ES2018 才有,Safari 直到 16.4 才支持
  • 锚点 \A \z:JS 完全没有
  • 内联修饰符 (?i):JS 不支持,标志只能挂在正则末尾
  • 独占量词 *+:JS 不支持

标签只打在真正有差异的条目上,没标签的就是各方言一致的通用语法。

常用模式那一类,请连注释一起看

页面里的 15 条常用模式(手机号、身份证、IPv4、日期、强密码……)每条都写明了它校验到什么程度。这点比模式本身重要:

  • 手机号那条只查「1 开头、第二位 3–9、共 11 位」,号段一直在增加,写死白名单必然过时
  • 身份证那条只校验格式,最后一位校验位要按 ISO 7064 的算法算,正则做不到
  • 日期那条拦不住 2 月 30 日,合法性得交给日期库
  • 邮箱根本不存在能校验干净的正则,唯一可靠的办法是发验证邮件

正则是过滤器,不是验证器。 它擅长挡住明显的手滑,不擅长确认一个值真实有效。

📍使用场景

  • 写到一半忘了某个语法怎么写断言、命名捕获组、Unicode 属性这些不天天用的,搜一下比翻文档快,每条都带中文释义和例子。
  • 网上抄来的正则在 JS 里跑不通标了「方言差异」的条目就是 JS 和 PCRE 不一致的地方,对着看一眼就知道是不是这个原因。
  • 要一条校验手机号、身份证、IPv4 的模式常用模式分类里 15 条现成的,每条都写清了它校验到什么程度、哪些情况它管不了。
  • 排查「为什么这个正则匹配不到中文」中文相关的坑集中在 Unicode 分类:反斜杠 w 不含中文、单词边界对中文无效、汉字区间写法的取舍。

常见问题

网上找的正则在 JS 里为什么跑不通?

多半是方言不同。 搜索结果里的正则大多按 PCRE(PHP / Perl)或 Python 的口径写,而浏览器和 Node 跑的是 JavaScript 引擎,几处常见的不兼容:\A \z \Z 这些锚点 JS 完全不认,要用 ^ $ 配合;内联修饰符 (?i) 不支持,标志只能写在正则末尾;独占量词 *+ 不支持;POSIX 字符类不支持。本页凡是有差异的条目都打了「方言差异」标签,照着改就行。

贪婪和非贪婪到底怎么选?

量词默认贪婪,会尽量多吃。<a>(.*)</a> 去抓一段含多个链接的 HTML,它会从第一个 <a> 一路匹配到最后一个 </a>,中间全被吞掉。在量词后面加 ? 变成惰性的 (.*?),才会在遇到第一个 </a> 时停下。经验法则:只要你的模式里有「直到遇见某个结束标记为止」的意思,量词就该是惰性的。

能用正则解析 HTML 或 JSON 吗?

不能,而且这不是写得够不够巧的问题。 HTML 和 JSON 都允许任意层级的嵌套,而正则(在没有递归扩展的情况下)表达不了「配对的括号」这种结构——这是形式语言层面的限制,不是技巧不够。实践后果很具体:注释里的假标签、属性值里的尖括号、自闭合标签、嵌套同名元素,每一样都能让看起来没问题的正则给出错误结果。HTML 用 DOMParser,JSON 用 JSON.parse。正则只适合在已经解析好的文本片段里做局部匹配。

什么是灾难性回溯?怎么避免?

嵌套量词遇上匹配失败时,引擎会尝试指数级数量的组合,页面直接卡死。 典型形状是 (a+)+$ 或者 (\s*\w+)*$ 这类「量词套量词」,拿一个长字符串去跑,回溯路径的数量随长度翻倍增长,几十个字符就能让浏览器主线程停几秒到几分钟。三条规避办法:① 别让两个量词嵌套;② 用具体的字符类代替 .,让失败尽早发生;③ 处理用户输入的正则时,放进 Web Worker 并设超时——本站的 Regex Pro 就是这么做的。

为什么反斜杠 w 匹配不到中文?

因为它的定义是 ASCII 的 [A-Za-z0-9_],就这么定死的。 同理,单词边界 \b 是按「单词字符和非单词字符的交界」定义的,中文两侧不构成边界,写 \b中文\b 基本不会是你想要的效果。要匹配汉字有两种写法:\p{Script=Han}(需要 u 标志,覆盖扩展区生僻字,推荐)和 [一-龥](不需要标志,但只覆盖基本区的一段,老代码里最常见)。

带 g 标志的正则,test() 为什么一次返回 true 一次 false?

因为带 g 的正则对象有 lastIndex 状态,它会记住上次匹配到哪。 复用同一个正则对象反复调 test()exec(),第二次会从上次结束的位置继续找,找不到就返回 false 并把 lastIndex 归零,于是下一次又成功——表现为真假交替。两种解法:每次现场新建正则字面量,或者在复用前手动 re.lastIndex = 0。把正则提到模块顶层当常量缓存时最容易踩到这个。