方括号 […]
内的多个字符或字符类表示“搜索给定字符中的任何字符”。
集合
例如,[eao]
表示 3 个字符中的任何一个:'a'
、'e'
或 'o'
。
这被称为集合。集合可以在正则表达式中与普通字符一起使用
// find [t or m], and then "op"
alert( "Mop top".match(/[tm]op/gi) ); // "Mop", "top"
请注意,虽然集合中有多个字符,但它们对应于匹配中的一个字符。
因此,以下示例没有匹配项
// find "V", then [o or i], then "la"
alert( "Voila".match(/V[oi]la/) ); // null, no matches
该模式搜索
V
,- 然后是字母
[oi]
中的一个, - 然后是
la
。
所以 Vola
或 Vila
会匹配。
范围
方括号也可以包含字符范围。
例如,[a-z]
是从 a
到 z
的范围内的字符,而 [0-5]
是从 0
到 5
的数字。
在下面的示例中,我们正在搜索 "x"
后面跟着两个数字或从 A
到 F
的字母
alert( "Exception 0xAF".match(/x[0-9A-F][0-9A-F]/g) ); // xAF
这里 [0-9A-F]
有两个范围:它搜索一个字符,该字符要么是 0
到 9
的数字,要么是 A
到 F
的字母。
如果我们想查找小写字母,我们可以添加 a-f
范围:[0-9A-Fa-f]
。或者添加标志 i
。
我们也可以在 […]
中使用字符类。
例如,如果我们想查找一个单词字符 \w
或一个连字符 -
,那么集合是 [\w-]
。
也可以组合多个类,例如 [\s\d]
表示“空格字符或数字”。
例如
- \d – 等同于
[0-9]
, - \w – 等同于
[a-zA-Z0-9_]
, - \s – 等同于
[\t\n\v\f\r ]
,以及其他一些罕见的 Unicode 空格字符。
示例:多语言 \w
由于字符类 \w
是 [a-zA-Z0-9_]
的简写,它无法找到汉字、西里尔字母等。
我们可以编写一个更通用的模式,它可以查找任何语言中的单词字符。使用 Unicode 属性很容易做到这一点:[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]
。
让我们来解读它。类似于 \w
,我们正在创建自己的集合,其中包含具有以下 Unicode 属性的字符
字母
(Alpha
) – 用于字母,Mark
(M
) – 用于重音符号,Decimal_Number
(Nd
) – 用于数字,Connector_Punctuation
(Pc
) – 用于下划线'_'
和类似字符,Join_Control
(Join_C
) – 两个特殊代码200c
和200d
,用于连字,例如在阿拉伯语中。
使用示例
let regexp = /[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]/gu;
let str = `Hi 你好 12`;
// finds all letters and digits:
alert( str.match(regexp) ); // H,i,你,好,1,2
当然,我们可以编辑此模式:添加或删除 Unicode 属性。Unicode 属性在文章 Unicode: flag "u" and class \p{...} 中有更详细的介绍。
IE 中未实现 Unicode 属性 p{…}
。如果我们确实需要它们,可以使用库 XRegExp。
或者只使用我们感兴趣的语言中的字符范围,例如 [а-я]
用于西里尔字母。
排除范围
除了正常的范围之外,还有“排除”范围,它们看起来像 [^…]
。
它们由开头处的插入符号字符 ^
表示,匹配除给定字符之外的任何字符。
例如
[^aeyo]
– 除'a'
、'e'
、'y'
或'o'
之外的任何字符。[^0-9]
– 除数字之外的任何字符,与\D
相同。[^\s]
– 任何非空格字符,与\S
相同。
下面的示例查找除字母、数字和空格之外的任何字符
alert( "[email protected]".match(/[^\d\sA-Z]/gi) ); // @ and .
在 […] 中转义
通常,当我们想要找到一个特殊的字符时,我们需要对其进行转义,例如 \.
。如果我们需要反斜杠,那么我们使用 \\
,等等。
在方括号中,我们可以使用绝大多数特殊字符而无需转义
- 符号
. + ( )
从不需要转义。 - 连字符
-
在开头或结尾处不转义(它不定义范围的地方)。 - 插入符号
^
仅在开头处转义(它表示排除的地方)。 - 右方括号
]
始终转义(如果我们需要查找该符号)。
换句话说,所有特殊字符都允许不转义,除非它们对方括号有特殊含义。
方括号内的点 .
仅表示一个点。模式 [.,]
将查找以下字符之一:点或逗号。
在下面的示例中,正则表达式 [-().^+]
查找以下字符之一:-().^+
// No need to escape
let regexp = /[-().^+]/g;
alert( "1 + 2 - 3".match(regexp) ); // Matches +, -
…但是,如果您决定“以防万一”对其进行转义,那么不会有任何问题
// Escaped everything
let regexp = /[\-\(\)\.\^\+]/g;
alert( "1 + 2 - 3".match(regexp) ); // also works: +, -
范围和标志“u”
如果集合中存在代理对,则需要标记 u
才能使它们正常工作。
例如,让我们在字符串 𝒳
中查找 [𝒳𝒴]
。
alert( '𝒳'.match(/[𝒳𝒴]/) ); // shows a strange character, like [?]
// (the search was performed incorrectly, half-character returned)
结果不正确,因为默认情况下正则表达式“不知道”代理对。
正则表达式引擎认为 [𝒳𝒴]
不是两个字符,而是四个字符。
𝒳
的左半部分(1)
,𝒳
的右半部分(2)
,𝒴
的左半部分(3)
,𝒴
的右半部分(4)
。
我们可以这样查看它们的代码
for(let i=0; i<'𝒳𝒴'.length; i++) {
alert('𝒳𝒴'.charCodeAt(i)); // 55349, 56499, 55349, 56500
};
因此,上面的示例找到了并显示了 𝒳
的左半部分。
如果我们添加标记 u
,则行为将是正确的。
alert( '𝒳'.match(/[𝒳𝒴]/u) ); // 𝒳
在查找范围时,例如 [𝒳-𝒴]
,也会出现类似的情况。
如果我们忘记添加标记 u
,就会出现错误。
'𝒳'.match(/[𝒳-𝒴]/); // Error: Invalid regular expression
原因是,如果没有标记 u
,代理对会被视为两个字符,因此 [𝒳-𝒴]
被解释为 [<55349><56499>-<55349><56500>]
(每个代理对都被替换为其代码)。现在很容易看出范围 56499-55349
是无效的:它的起始代码 56499
大于结束代码 55349
。这是错误的正式原因。
使用标记 u
,模式可以正常工作。
// look for characters from 𝒳 to 𝒵
alert( '𝒴'.match(/[𝒳-𝒵]/u) ); // 𝒴
评论
<code>
标签,对于多行代码,请使用<pre>
标签,对于超过 10 行的代码,请使用沙箱(plnkr,jsbin,codepen…)