8集合和范围[...]
前言
在方括号 […] 中的几个字符或者字符类意味着“搜索给定的字符中的任意一个”。
集合
比如说,[eao] 意味着查找在 3 个字符 'a'、'e' 或者 `‘o’ 中的任意一个。
这被叫做一个集合。集合可以在正则表达式中和其它常规字符一起使用。
// 查找 [t 或者 m],然后再匹配 “op”
console.log( "Mop top".match(/[tm]op/gi) ); // [ 'Mop', 'top' ]请注意尽管在集合中有多个字符,但它们在匹配中只会对应其中的一个。
所以下面的示例并不会匹配上:
// 查找 “V”,然后匹配 [o 或者 i],之后再匹配 “la”
console.log( "Voila".match(/V[oi]la/) ); // null,并没有匹配上这个模式会做以下假设:
V,- 然后匹配其中的一个字符
[oi], - 然后匹配
la,
所以可以匹配上 Vola 或者 Vila。
范围
方括号也可以包含字符范围。
比如说,[a-z] 会匹配从 a 到 z 范围内的字母,[0-5] 表示从 0 到 5 的数字。
在下面的示例中,我们会查询首先匹配 "x" 字符,再匹配两个数字或者位于 A 到 F 范围内的字符。
console.log( "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 字符:
Alphabetic(Alpha) —— 字母,Mark(M) —— 重读,Decimal_Number(Nd) —— 数字,Connector_Punctuation(Pc) —— 下划线'_'和类似的字符,Join_Control(Join_C) —— 两个特殊代码200cand200d,用于连字,例如阿拉伯语。
使用示例:
let regexp = /[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]/gu;
let str = `Hi 你好 12`;
// finds all letters and digits:
console.log( str.match(regexp) ); // H,i,你,好,1,2当然,我们可以编辑此模式:添加 unicode 属性或删除它们。文章 Unicode:修饰符 “u” 和 class \p{...} 中包含了更多 Unicode 属性的细节。
Edge 和 Firefox 不支持 Unicode 属性
Edge 和 Firefox 尚未实现 Unicode 属性
p{…}。如果确实需要它们,可以使用库 XRegExp。或者只使用我们想要的语言范围的字符,例如西里尔字母
[а-я]。
排除范围
除了普通的范围匹配,还有类似 [^…] 的“排除”范围匹配。
它们通过在匹配查询的开头添加插入符号 ^ 来表示,它会匹配所有除了给定的字符之外的任意字符。
比如说:
[^aeyo]—— 匹配任何除了'a'、'e'、'y'或者'o'之外的字符。[^0-9]—— 匹配任何除了数字之外的字符,也可以使用\D来表示。[^\s]—— 匹配任何非空字符,也可以使用\S来表示。
下面的示例查询除了字母,数字和空格之外的任意字符:
console.log( "alice15@gmail.com".match(/[^\d\sA-Z]/gi) ); // [ '@', '.' ]在[...]中不转义
通常当我们的确需要查询点字符时,我们需要把它转义成像 \. 这样的形式。如果我们需要查询一个反斜杠,我们需要使用 \\。
在方括号表示中,绝大多数特殊字符可以在不转义的情况下使用:
- 表示一个点符号
'.'。 - 表示一个加号
'+'。 - 表示一个括号
'( )'。 - 在开头或者结尾表示一个破折号(在这些位置该符号表示的就不是一个范围) `pattern:’-’。
- 在不是开头的位置表示一个插入符号(在开头位置该符号表示的是排除)
'^'。 - 表示一个开口的方括号符号
'['。
换句话说,除了在方括号中有特殊含义的字符外,其它所有特殊字符都是允许不添加反斜杠的。
一个在方括号中的点符号 "." 表示的就是一个点字符。查询模式 [.,] 将会寻找一个为点或者逗号的字符。
在下面的示例中,[-().^+] 会查找 -().^+ 的其中任意一个字符:
// 并不需要转义
let reg = /[-().^+]/g;
console.log( "1 + 2 - 3".match(reg) ); // [ '+', '-' ]。。。但是如果你为了“以防万一”转义了它们,这也不会有任何问题:
//转义其中的所有字符
let reg = /[\-\(\)\.\^\+]/g;
console.log( "1 + 2 - 3".match(reg) ); // 仍能正常工作:[ '+', '-' ]范围和标志"u"
如果集合中有代理对(surrogate pairs),则需要标志 u 以使其正常工作。
例如,让我们在字符串 𝒳 中查找 [𝒳𝒴]:
console.log('𝒳'.match(/[𝒳𝒴]/)); // 显示一个奇怪的字符,像 [?]
// [ '�', index: 0, input: '𝒳', groups: undefined ]
//(搜索执行不正确,返回了半个字符)结果不正确,因为默认情况下正则表达式“不知道”代理对。
正则表达式引擎认为 [𝒳𝒴] —— 不是两个,而是四个字符:
𝒳(1)的左半部分,𝒳(2)的右半部分,𝒴(3)的左半部分,𝒴(4)的右半部分。
我们可以看到它们的代码,如下所示:
for (let i = 0; i < '𝒳𝒴'.length; i++) {
console.log('𝒳𝒴'.charCodeAt(i)); // 55349, 56499, 55349, 56500
};因此,以上示例查找并显示了 𝒳 的左半部分。
如果我们添加标志 u,那么行为将是正确的:
console.log( '𝒳'.match(/[𝒳𝒴]/u) ); // [ '𝒳', index: 0, input: '𝒳', groups: undefined ]当我们查找范围时也会出现类似的情况,就像 [𝒳-𝒴]。
如果我们忘记添加标志 u,则会出现错误:
console.log('𝒳'.match(/[𝒳-𝒴]/)); // 错误:无效的正则表达式
// SyntaxError: Invalid regular expression: /[𝒳-𝒴]/: Range out of order in character class原因是,没有标志 u 的代理对被视为两个字符,因此 [𝒳-𝒴] 被解释为 [<55349><56499>-<55349><56500>](每个代理对都替换为其代码)。现在很容易看出范围 56499-55349 是无效的:其起始代码 56499 大于终止代码 55349。这就是错误的原因。
使用标志 u,该模式可以正常匹配:
// 查找字符从 𝒳 到 𝒵
console.log('𝒴'.match(/[𝒳-𝒵]/u)); // [ '𝒴', index: 0, input: '𝒴', groups: undefined ]总结
正则表达式中,使用方括号包裹的字符,类似于
[abcd],表示满足其中一个字符即可如果想取反的话,可以在方括号中使用
^,比如[^0-9],表示不是0-9中的任意一个方括号中的
-表示范围,比如[0-9]就是[0123456789]一些简写方式
\d:数字集合,也可以表示为[0-9],所以\D可以表示为[^0-9]\s:空格符号,包括空格、制表符\t、换行符\n,和其它少数的稀有字符,也可以表示为,和[\t\n\v\f\r ]外加少量罕见的 unicode 空格字符。(\v:垂直标签,\f:换页,\r:Windows文本文件使用两个字符\r\n表示换行)\w:一个单词,也可以表示为[a-zA-Z0-9_],所以\W,非单字符可以表示为[^a-zA-Z0-9_]
一些特殊字符的范围表示,必须加修饰符
u,比如[/[𝒳-𝒵]/u],如果不标识u不能正常识别,且有可能会报错。这是因为,没有标识u的代理对被视为两个字符,因此那些四个字符的无法正确识别。那些不标识
u的特殊字符,在进行范围匹配时,有可能会转换为这样的形式[<55349><56499>-<55349><56500>],这时的范围是56499-55349,左边的大于右边,区间错误,于是就会报错。如果想在方括号里匹配特殊字符,大多数的特殊字符可以转义也可以不转义,都会被正常识别,比如
jslet str = '^.\\()-+/' // 方括号中的部分特殊字符,无需转义也能正常获取到,不过反斜杠需要转义一下 console.log(str.match(/[.\\()\-+^]/g)); // [ '^', '.', '\\', '(', ')', '-', '+' ] // 正常转义也是可以的 console.log(str.match(/[\.\\\(\)\-\+\^]/g)); // [ '^', '.', '\\', '(', ')', '-', '+' ]注意:因为方括号中的
^放开头,表示取反,-放中间表示范围,所以在匹配它俩的时候需要注意一下,如果想匹配开头的^就需要转义一下,同理匹配中间的-时也需要转义一下。因为
\w是简写的[a-zA-Z0-9_],所以无法找到象形文字或者西里尔字母等,所以可以使用unicode的\p{}属性,编写更通用的模式去匹配所有的文字字符,比如[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]。Alphabetic(Alpha) —— 字母,Mark(M) —— 重读,Decimal_Number(Nd) —— 数字,Connector_Punctuation(Pc) —— 下划线'_'和类似的字符,Join_Control(Join_C) —— 两个特殊代码200cand200d,用于连字,例如阿拉伯语。
.会匹配到所有的字符,除了换行符,即\n或者\r\njslet str2 = "abc123 你好 \n 𝒳𝒴 \r\n cc \t 11 \f 22 \v 333" console.log(str2.match(/.+/g)); // [ 'abc123 你好 ', ' 𝒳𝒴 ', ' cc \t 11 \f 22 \x0B 333' ]