字符类:[...],[^...]
一个 **字符类** 匹配自定义字符集中任何存在或不存在的字符。当 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]
匹配除 a
、b
或 c
之外的任何字符。^
字符在字符类中间时是一个字面量字符 - 例如,[a^b]
匹配字符 a
、^
和 b
。
词法语法 对正则表达式字面量进行非常粗略的解析,因此它不会在字符类中出现的 /
字符处结束正则表达式字面量。这意味着 /[/]/
是有效的,不需要转义 /
。
字符范围的边界不能指定多个字符,如果使用 字符类转义,就会发生这种情况。例如
/[\s-9]/u; // SyntaxError: Invalid regular expression: Invalid character class
在 Unicode 非感知模式 中,其中一个边界是字符类的字符范围会使 -
成为字面量字符。这是一个 用于 Web 兼容性的已弃用语法,你不应该依赖它。
/[\s-9]/.test("-"); // true
在 Unicode 非感知模式 中,正则表达式被解释为 BMP 字符的序列。因此,字符类中的代理对表示两个字符而不是一个字符。
/[😄]/.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
仅匹配 E
、F
、e
和 f
,而模式 /[E-f]/i
匹配所有大写和小写 ASCII 字母(因为它跨越 E–Z
和 a–f
),以及 [
、\
、]
、^
、_
和 `
。
非 v 模式字符类
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
,但它仍然被拒绝。
补码类和不区分大小写的匹配
在非v
模式下,补码字符类[^...]
是通过简单地反转匹配结果来实现的 - 也就是说,[^...]
在[...]
不匹配时匹配,反之亦然。但是,其他补码类,例如 \P{...}
和 \W
,通过急切地构建由所有没有指定属性的字符组成的集合来工作。它们似乎产生相同的行为,但在与不区分大小写 匹配结合使用时变得更加复杂。
考虑以下两个正则表达式
const r1 = /\p{Lowercase_Letter}/iu;
const r2 = /[^\P{Lowercase_Letter}]/iu;
r2
是双重否定,似乎与 r1
等价。但实际上,r1
匹配所有大小写 ASCII 字母,而 r2
不匹配任何字母。为了说明它是如何工作的,假设我们只处理 ASCII 字符,而不是整个 Unicode 字符集,r1
和 r2
如下所示
const r1 = /[a-z]/iu;
const r2 = /[^A-Z]/iu;
回想一下,不区分大小写的匹配是通过将模式和输入折叠到相同的大小写来实现的(有关更多详细信息,请参见ignoreCase
)。对于 r1
,字符类 a-z
在大小写折叠后保持不变,而大小写 ASCII 字符串输入都被折叠为小写,因此 r1
能够匹配 "A"
和 "a"
。对于 r2
,字符类 A-Z
被折叠为 a-z
;但是,^
否定匹配结果,因此 [^A-Z]
实际上只匹配大写字符串。但是,大小写 ASCII 字符串输入仍然被折叠为小写,导致 r2
不匹配任何内容。
在v
模式下,此行为已修复 - [^...]
也急切地构建补码类,而不是否定匹配结果。这使得 [^\P{Lowercase_Letter}]
和 \p{Lowercase_Letter}
严格等价。
示例
匹配十六进制数字
以下函数确定字符串是否包含有效的十六进制数字
function isHexadecimal(str) {
return /^[0-9A-F]+$/i.test(str);
}
isHexadecimal("2F3"); // true
isHexadecimal("beef"); // true
isHexadecimal("undefined"); // false
使用交集
以下函数匹配希腊字母。
function greekLetters(str) {
return str.match(/[\p{Script_Extensions=Greek}&&\p{Letter}]/gv);
}
// 𐆊 is U+1018A GREEK ZERO SIGN
greekLetters("π𐆊P0零αAΣ"); // [ 'π', 'α', 'Σ' ]
使用差集
以下函数匹配所有非 ASCII 数字。
function nonASCIINumbers(str) {
return str.match(/[\p{Decimal_Number}--[0-9]]/gv);
}
// 𑜹 is U+11739 AHOM DIGIT NINE
nonASCIINumbers("𐆊0零1𝟜𑜹a"); // [ '𝟜', '𑜹' ]
匹配字符串
以下函数匹配所有行终止符序列,包括行终止符字符 和序列 \r\n
(CRLF)。
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{}
最有用的情况是在执行差集和交集时。以前,可以使用多个前瞻 来实现这一点。以下函数匹配不是美国、中国、俄罗斯、英国和法国国旗的国旗。
function notUNSCPermanentMember(flag) {
return /^[\p{RGI_Emoji_Flag_Sequence}--\q{🇺🇸|🇨🇳|🇷🇺|🇬🇧|🇫🇷}]$/v.test(flag);
}
notUNSCPermanentMember("🇺🇸"); // false
notUNSCPermanentMember("🇩🇪"); // true
此示例与 /^(?!🇺🇸|🇨🇳|🇷🇺|🇬🇧|🇫🇷)\p{RGI_Emoji_Flag_Sequence}$/v
大致等价,但可能性能更高。
规范
规范 |
---|
ECMAScript 语言规范 # prod-CharacterClass |
浏览器兼容性
BCD 表格仅在浏览器中加载
另请参阅
- 字符类 指南
- 正则表达式
- 字符类转义:
\d
、\D
、\w
、\W
、\s
、\S
- Unicode 字符类转义:
\p{...}
、\P{...}
- 文字字符:
a
、b
- 字符转义:
\n
、\u{...}
- 并列:
|
- 带有集合表示法和字符串属性的 RegExp v 标志 在 v8.dev 上(2022 年)