字符类:[...], [^...]

Baseline 已广泛支持

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2015 年 7 月⁩以来,各浏览器均已提供此特性。

字符类匹配自定义字符集中包含或不包含的任何字符。当v 标志启用时,它还可以用于匹配有限长度的字符串。

语法

正则表达式
[]
[abc]
[A-Z]

[^]
[^abc]
[^A-Z]

// `v` mode only
[operand1&&operand2]
[operand1--operand2]
[\q{substring}]

参数

operand1, operand2

可以是单个字符、另一个用方括号括起来的字符类、一个字符类转义、一个Unicode 字符类转义,或使用 \q 语法的字符串。

子字符串

一个字面量字符串。

描述

字符类指定方括号内的字符列表,并匹配列表中的任何字符。v 标志极大地改变了字符类的解析和解释方式。以下语法在 v 模式和非 v 模式下均可用:

  • 单个字符:匹配字符本身。
  • 字符范围:匹配包含范围内的任何字符。范围由两个字符指定,中间用连字符(-)分隔。第一个字符的字符值必须小于第二个字符。字符值是字符的 Unicode 码点。由于 Unicode 码点通常按字母顺序分配,[a-z] 指定所有小写拉丁字符,而 [α-ω] 指定所有小写希腊字符。在非 Unicode 感知模式下,正则表达式被解释为一系列 BMP 字符。因此,字符类中的代理对表示两个字符而不是一个;详见下文。
  • 转义序列:\b\-字符类转义Unicode 字符类转义以及其他字符转义

这些语法可以出现任意次数,并且它们所代表的字符集是并集。例如,/[a-zA-Z0-9]/ 匹配任何字母或数字。

字符类中的 ^ 前缀会创建一个补集。例如,[^abc] 匹配除了 abc 之外的任何字符。当 ^ 字符出现在字符类中间时,它是一个字面量字符 — 例如,[a^b] 匹配字符 a^b

词法语法对正则表达式字面量进行非常粗略的解析,以便它不会在字符类中出现的 / 字符处结束正则表达式字面量。这意味着 /[/]/ 是有效的,无需转义 /

字符范围的边界不能指定多个字符,这在使用字符类转义时会发生。例如:

js
/[\s-9]/u; // SyntaxError: Invalid regular expression: Invalid character class

非 Unicode 感知模式下,如果字符范围的其中一个边界是字符类,则 - 会变成字面量字符。这是为了 Web 兼容性而弃用的语法,不应依赖它。

js
/[\s-9]/.test("-"); // true

非 Unicode 感知模式下,正则表达式被解释为一系列 BMP 字符。因此,字符类中的代理对表示两个字符而不是一个。

js
/[😄]/.test("\ud83d"); // true
/[😄]/u.test("\ud83d"); // false

/[😄-😛]/.test("😑"); // SyntaxError: Invalid regular expression: /[😄-😛]/: Range out of order in character class
/[😄-😛]/u.test("😑"); // true

即使模式忽略大小写,范围两端的大小写对于确定哪些字符属于该范围仍然很重要。例如,模式 /[E-F]/i 仅匹配 EFef,而模式 /[E-f]/i 匹配所有大写和小写 ASCII 字母(因为它涵盖了 E–Za–f),以及 [\]^_`

非 v 模式字符类

v 模式字符类将大多数字符按字面量解释,并且对其可包含的字符限制较少。例如,. 是字面量点字符,而不是通配符。唯一不能按字面量出现的字符是 \]-

  • 在字符类中,大多数转义序列都受支持,除了 \b\B反向引用\b 表示退格字符而不是单词边界,而另外两个会导致语法错误。要按字面量使用 \,请将其转义为 \\
  • 字符 ] 表示字符类的结束。要按字面量使用它,请将其转义为 \]
  • 连字符(-)字符,当用于两个字符之间时,表示一个范围。当它出现在字符类的开头或结尾时,它是一个字面量字符。当它用于范围的边界时,它也是一个字面量字符。例如,[a-] 匹配字符 a-[!--] 匹配字符 !-,而 [--9] 匹配字符 -9。如果你想在任何地方按字面量使用它,你也可以将其转义为 \-

v 模式字符类

v 模式下字符类的基本思想保持不变:您仍然可以使用大多数字符的字面量形式,使用 - 表示字符范围,并使用转义序列。v 标志最重要的特性之一是字符类中的集合表示法。如前所述,普通字符类可以通过连接两个范围来表达并集,例如使用 [A-Z0-9] 表示“集合 [A-Z] 和集合 [0-9] 的并集”。然而,没有简单的方法可以表示字符集的其他操作,例如交集和差集。

使用 v 标志,交集用 && 表示,减法用 -- 表示。两者都不存在意味着并集。&&-- 的两个操作数可以是字符、字符转义、字符类转义,甚至是另一个字符类。例如,要表示“不是下划线的单词字符”,您可以使用 [\w--_]。您不能在同一级别混合操作符。例如,[\w&&[A-z]--_] 是一个语法错误。然而,由于您可以嵌套字符类,您可以通过编写 [\w&&[[A-z]--_]][[\w&&[A-z]]--_] 来明确(两者都表示 [A-Za-z])。类似地,[AB--C] 是无效的,您需要编写 [A[B--C]](这只表示 [AB])。

v 模式下,Unicode 字符类转义 \p 可以匹配有限长度的字符串,例如表情符号。为了对称性,常规字符类也可以匹配多个字符。要在字符类中写入“字符串字面量”,您需要将字符串用 \q{...} 包裹起来。这里唯一支持的正则表达式语法是析取——除此之外,\q 必须完全包含字面量(包括转义字符)。这确保了字符类只能匹配具有有限多种可能性的有限长度字符串。

由于字符类语法现在更加复杂,因此更多的字符被保留并禁止以字面量形式出现。

  • 除了 ]\ 之外,如果以下字符表示字面量字符,则必须在字符类中进行转义:()[{}/-|。此列表与语法字符列表有些相似,不同之处在于 ^$*+? 在字符类内部不保留,而 /- 在字符类外部不保留(尽管 / 可能会分隔正则表达式字面量,因此仍然需要转义)。所有这些字符也可以在 u 模式字符类中选择性地转义。
  • 以下“双标点”序列也必须转义(但无论如何,没有 v 标志它们都没有太大意义):&&!!##$$%%**++,,..::;;<<==>>??@@^^``~~。在 u 模式下,其中一些字符只能在字符类中以字面量形式出现,并在转义时导致语法错误。在 v 模式下,当它们成对出现时必须转义,但单独出现时可以选择转义。例如,/[\!]/u 无效,因为它是一个恒等转义,但 /[\!]/v/[!]/v 都有效,而 /[!!]/v 无效。字面量字符参考有一个详细的表格,说明哪些字符可以转义或不转义出现。

补集字符类 [^...] 不可能匹配长度超过一个字符的字符串。例如,[\q{ab|c}] 是有效的,并匹配字符串 "ab",但 [^\q{ab|c}] 是无效的,因为不清楚应该消耗多少个字符。检查是通过检查所有 \q 是否包含单个字符以及所有 \p 是否指定字符属性来完成的——对于并集,所有操作数必须是纯字符;对于交集,至少一个操作数必须是纯字符;对于减法,最左边的操作数必须是纯字符。该检查是语法上的,不考虑实际指定的字符集,这意味着尽管 /[^\q{ab|c}--\q{ab}]/v 等同于 /[^c]/v,它仍然被拒绝。

补集字符类和不区分大小写的匹配

不区分大小写匹配通过对预期字符集和匹配字符串进行大小写折叠来实现。指定补集字符类时,JavaScript 执行大小写折叠和补集的顺序很重要。简而言之,u 模式下的 [^...] 匹配 所有字符 - caseFold(原始),而 v 模式下的 [^...] 匹配 caseFold(所有字符) - caseFold(原始)。这确保了所有补集字符类语法,包括 [^...]\P\W 等,都能相互抵消。

考虑以下两个正则表达式(为简化起见,我们假设 Unicode 字符有三种:小写、大写和无大小写,并且每个大写字母都有唯一的小写对应,反之亦然)

js
const r1 = /\p{Lowercase_Letter}/iu;
const r2 = /[^\P{Lowercase_Letter}]/iu;

r2 是双重否定,似乎与 r1 等效。但实际上,r1 匹配所有小写和大写 ASCII 字母,而 r2 一个也不匹配。以下是逐步解释:

  • r1 中,\p{Lowercase_Letter} 构造了一个包含所有小写字符的集合。此集合中的字符随后被大小写折叠为其小写形式,因此它们保持不变。输入字符串也被大小写折叠为小写。因此,"A""a" 都被折叠为 "a" 并被 r1 匹配。
  • r2 中,\P{Lowercase_Letter} 首先构造一个包含所有非小写字符的集合,即大写字母和无大小写字符。此集合中的字符随后被大小写折叠为它们的小写形式,因此字符集变为所有小写字母和无大小写字符。[^...] 否定了匹配,使其匹配在此集合中的任何内容,即大写字母。然而,输入仍然被大小写折叠为小写,因此 "A" 被折叠为 "a" 并且不被 r2 匹配。

这里的主要观察是,在 [^...] 否定匹配之后,预期的字符集可能不是大小写折叠后的 Unicode 字符集的子集,导致大小写折叠后的输入不在预期的字符集中。在 v 模式下,所有字符集也被大小写折叠。\P 字符类本身在 v 模式下的工作方式也略有不同(请参阅Unicode 字符类转义)。所有这些都确保了 [^\P{Lowercase_Letter}]\p{Lowercase_Letter} 严格等效。

示例

匹配十六进制数字

以下函数确定字符串是否包含有效的十六进制数字

js
function isHexadecimal(str) {
  return /^[0-9A-F]+$/i.test(str);
}

isHexadecimal("2F3"); // true
isHexadecimal("beef"); // true
isHexadecimal("undefined"); // false

使用交集

以下函数匹配希腊字母。

js
function greekLetters(str) {
  return str.match(/[\p{Script_Extensions=Greek}&&\p{Letter}]/gv);
}

// 𐆊 is U+1018A GREEK ZERO SIGN
greekLetters("π𐆊P0零αAΣ"); // [ 'π', 'α', 'Σ' ]

使用减法

以下函数匹配所有非 ASCII 数字。

js
function nonASCIINumbers(str) {
  return str.match(/[\p{Decimal_Number}--\d]/gv);
}

// 𑜹 is U+11739 AHOM DIGIT NINE
nonASCIINumbers("𐆊0零1𝟜𑜹a"); // [ '𝟜', '𑜹' ]

匹配字符串

以下函数匹配所有行终止符序列,包括行终止符字符和序列 \r\n (CRLF)。

js
function getLineTerminators(str) {
  return str.match(/[\r\n\u2028\u2029\q{\r\n}]/gv);
}

getLineTerminators(`
A poem\r
Is split\r\n
Into many
Stanzas
`); // [ '\r', '\r\n', '\n' ]

这个例子与 /(?:\r|\n|\u2028|\u2029|\r\n)/gu/(?:[\r\n\u2028\u2029]|\r\n)/gu 完全等效,只是更短。

\q{} 最有用的情况是在执行减法和交集时。以前,这可以通过多个先行断言来实现。以下函数匹配不是美国、中国、俄罗斯、英国和法国国旗的旗帜。

js
function notUNSCPermanentMember(flag) {
  return /^[\p{RGI_Emoji_Flag_Sequence}--\q{🇺🇸|🇨🇳|🇷🇺|🇬🇧|🇫🇷}]$/v.test(flag);
}

notUNSCPermanentMember("🇺🇸"); // false
notUNSCPermanentMember("🇩🇪"); // true

这个例子大致等同于 /^(?!🇺🇸|🇨🇳|🇷🇺|🇬🇧|🇫🇷)\p{RGI_Emoji_Flag_Sequence}$/v,但可能性能更好。

规范

规范
ECMAScript® 2026 语言规范
# prod-CharacterClass

浏览器兼容性

另见