全局匹配,找到所有匹配,而不是在第一个匹配后停止。
请注意只有开启了g
修饰符,lastIndex
的值才会被维护,和设置及时生效。
下面案例中详细说明了,设置了g
修饰符和未设置的区别。
当设置了g
修饰符,exec
会自动找到下一个匹配项
,但是如果没有设置过,则不会寻找下一个匹配项
。
((log) => {
const str = 'ababa';
const reg1 = /a/;
const reg2 = /a/g;
log(reg1.exec(str)); // ["a", index: 0, input: "ababa", groups: undefined]
log(reg1.lastIndex); // 0
reg1.lastIndex = 1;
log(reg1.exec(str)); // ["a", index: 0, input: "ababa", groups: undefined]
log(reg1.lastIndex); // 1
log(reg2.exec(str)); // ["a", index: 0, input: "ababa", groups: undefined]
log(reg2.lastIndex); // 1
reg2.lastIndex = 1;
log(reg2.exec(str)); // ["a", index: 2, input: "ababa", groups: undefined]
log(reg2.lastIndex); // 3
})(console.log)
忽略需要匹配项中的字母大小写
((log) => {
const str = 'aba';
const reg1 = /aB/;
const reg2 = /aB/i;
log(reg1.exec(str)); // null
log(reg2.exec(str)); // ["ab", index: 0, input: "aba", groups: undefined]
})(console.log)
m
标志意味着一个多行
输入字符串被看作多行
。
例如,使用 m
,^
和 $
将会从只匹配正则字符串
的开头
或结尾
,变为匹配字符串
中任一行
的开头
或结尾
。
((log) => {
const s = "Please yes\nmake my day!";
log(s.match(/yes.*day/)); // null
log(s.match(/yes[^]*day/)); // ["yes↵make my day", index: 7, input: "Please yes↵make my day!", groups: undefined]
})(console.log)
正常情况下\w
或 \W
只会匹配基本的 ASCII
字符,如 a
到 z
'、 A
到 Z
、 0
到 9
及 _
。
为了匹配其他语言中的字符,如西里尔(Cyrillic)
或 希伯来语(Hebrew)
,要使用 \uhhhh
,hhhh
表示以十六进制
表示的字符的 Unicode
值。
ES6
对正则表达式
添加了u
修饰符,含义为Unicode 模式
,用来正确处理大于\uFFFF
的 Unicode
字符。也就是说,会正确处理四个字节的 UTF-16
编码。
一旦加上u
修饰符号,就会修改下面这些正则表达式的行为。
ES6
新增了使用大括号
表示 Unicode
字符,这种表示法
在正则表达式
中必须加上u
修饰符,才能识别当中的大括号
,否则会被解读为前一个匹配项
的量词
。
((log) => {
log(/\u{61}/.test('a')); // false
log(/\u{61}/u.test('a')); // true
log(/\u{20BB7}/u.test('𠮷')); // true
})(console.log)
上面代码表示,如果不加u
修饰符,正则表达式
无法识别\u{61}
这种表示法,只会认为这匹配 61
个连续的u
。
/^\uD83D/u.test('\uD83D\uDC2A') // false
/^\uD83D/.test('\uD83D\uDC2A') // true
上面代码中,\uD83D\uDC2A
是一个四个字节的 UTF-16
编码,代表一个字符
。
加了u
修饰符以后,ES6
就会识别其为一个字符
,所以第一行代码结果为false
。
但是,ES5
不支持四个字节的 UTF-16
编码,会将其识别为两个字符
,导致第二行代码结果为true
。
展示了怎样从一个单词
中分离出 Unicode
字符。
((log) => {
const text = "Образец text на русском языке";
const regex = /[\u0400-\u04FF]+/g;
const match = regex.exec(text);
log(match[0]); // logs "Образец"
log(regex.lastIndex); // logs "7"
const match2 = regex.exec(text);
log(match2[0]); // logs "на" [did not log "text"]
log(regex.lastIndex); // logs "15"
// and so on
})(console.log)
这里有一个外部资源,用来获取 Unicode
中的不同区块范围:Regexp-unicode-block
((log) => {
const url = "http://xxx.domain.com";
log(/[^.]+/.exec(url)[0].substr(7)); // logs "xxx"
})(console.log)
点(.
)字符在正则表达式
中,含义是除了换行符
以外的任意单个字符
。对于码点
大于0xFFFF
的 Unicode
字符,点字符
不能识别,必须加上u
修饰符。
((log) => {
const s = '𠮷';
log(/^.$/.test(s)); // false
log(/^.$/u.test(s)); // true
})(console.log)
上面代码表示,如果不添加u
修饰符,正则表达式
就会认为字符串为两个字符
,从而匹配失败。
使用u
修饰符后,所有量词
都会正确识别码点大于0xFFFF
的 Unicode
字符。
((log) => {
log(/a{2}/.test('aa')); // true
log(/a{2}/u.test('aa')); // true
log(/𠮷{2}/.test('𠮷𠮷')); // false
log(/𠮷{2}/u.test('𠮷𠮷')); // true
})(console.log)
u
修饰符也影响到预定义模式
,能否正确识别码点大于0xFFFF
的 Unicode
字符。
((log) => {
log(/^\S$/.test('𠮷')); // false
log(/^\S$/u.test('𠮷')); // true
})(console.log)
上面代码的\S
是预定义模式,匹配所有非空白字符
。只有加了u
修饰符,它才能正确匹配码点大于0xFFFF
的 Unicode
字符。
利用这一点,可以写出一个正确返回字符串长度的函数。
((log) => {
const codePointLength = (text) => {
let result = text.match(/[\s\S]/gu);
return result ? result.length : 0;
}
const s = '𠮷𠮷';
log(s.length); // 4
log(codePointLength(s)); // 2
})(console.log)
有些 Unicode
字符的编码不同,但是字型很相近,比如,\u004B
与\u212A
都是大写的K
。
((log) => {
log(/[a-z]/i.test('\u212A')); // false
log(/[a-z]/iu.test('\u212A')); // true
})(console.log)
上面代码中,不加u
修饰符,就无法识别非规范
的K
字符。
ES6
还为正则表达式添加了y
修饰符,叫做粘连
(sticky)修饰符。
y
修饰符的作用与g
修饰符类似,也是全局匹配
,后一次匹配都从上一次匹配成功的下一个位置开始。
不同之处在于,g
修饰符只要剩余位置
中存在匹配
就可,而y
修饰符确保匹配必须从剩余
的第一个位置开始,这也就是粘连
的涵义。
g
修饰符关联 #((log) => {
const s = 'aaa_aa_a';
const r1 = /a+/g;
const r2 = /a+/y;
log(r1.exec(s)); // ["aaa", index: 0, input: "aaa_aa_a", groups: undefined]
log(r2.exec(s)); // ["aaa", index: 0, input: "aaa_aa_a", groups: undefined]
log(r1.exec(s)); // ["aa", index: 4, input: "aaa_aa_a", groups: undefined]
log(r2.exec(s)); // null
})(console.log)
上面代码有两个正则表达式,一个使用g
修饰符,另一个使用y
修饰符。
这两个正则表达式各执行了两次,第一次执行的时候,两者行为相同,剩余字符串都是_aa_a
。
由于g
修饰没有位置要求,所以第二次执行会返回结果,而y
修饰符要求匹配必须从头部开始,所以返回null
。
实际上,y
修饰符号隐含了头部匹配的标志^
。
即保证每次都能头部匹配
,y
修饰符就会返回结果了。
((log) => {
log(/b/y.exec('aba')); // null
const s = 'aaa_aa_a';
const r = /a+_/y;
log(r.exec(s)); // ["aaa_", index: 0, input: "aaa_aa_a", groups: undefined]
log(r.exec(s)); // ["aa_", index: 4, input: "aaa_aa_a", groups: undefined]
})(console.log)
第一段代码
由于不能保证头部匹配
,所以返回null
。y
修饰符的设计本意,就是让头部匹配
的标志^
在全局匹配
中都有效。
下面代码中,lastIndex
属性指定每次搜索的开始位置,g
修饰符从这个位置开始向后搜索,直到发现匹配为止。
((log) => {
const REGEX = /a/y;
// 指定从2号位置开始匹配
REGEX.lastIndex = 2;
// 不是粘连,匹配失败
log(REGEX.exec('xaya')); // null
// 指定从3号位置开始匹配
REGEX.lastIndex = 3;
// 3号位置是粘连,匹配成功
const match = REGEX.exec('xaya');
log(match.index); // 3
log(REGEX.lastIndex);// 4
})(console.log)
下面代码中,最后一个a
因为不是出现在下一次匹配的头部
,所以不会被替换。
((log) => {
const REGEX = /a/gy;
log('aaxa'.replace(REGEX, '-')); // '--xa'4
})(console.log)
单单一个y
修饰符对match
方法,只能返回第一个匹配
,必须与g
修饰符联用,才能返回所有匹配。
((log) => {
log('a1a2a3'.match(/a\d/y)); // ["a1"]
log('a1a2a3'.match(/a\d/gy)); // ["a1", "a2", "a3"]
})(console.log)
结合正则表达式
上使用 sticky
标志,用来匹配多行输入的单独行。
((log) => {
const text = "First line\nsecond line";
const regex = /(\S+) line.?/ys;
const match = regex.exec(text);
log(match); // ["First line↵", "First", index: 0, input: "First line↵second line", groups: undefined]
log(match[1]); // "First"
log(regex.lastIndex); // 11
const match2 = regex.exec(text);
log(match2); // ["second line", "second", index: 11, input: "First line↵second line", groups: undefined]
log(match2[1]); // "Second"
log(regex.lastIndex); // "22"
const match3 = regex.exec(text);
log(match3 === null); // "true"
})(console.log)
是从字符串提取 token
(词元),y
修饰符确保了匹配之间不会有漏掉的字符
。
((log) => {
const tokenize = (TOKEN_REGEX, str) => {
const result = [];
let match;
while (match = TOKEN_REGEX.exec(str)) {
result.push(match[1]);
}
return result;
}
const TOKEN_Y = /\s*(\+|[0-9]+)\s*/y;
const TOKEN_G = /\s*(\+|[0-9]+)\s*/g;
log(tokenize(TOKEN_Y, '3 + 4'));
// [ '3', '+', '4' ]
log(tokenize(TOKEN_G, '3 + 4'));
// [ '3', '+', '4' ]
})(console.log)
上面代码中,如果字符串里面没有非法字符,y
修饰符与g
修饰符的提取结果是一样的。
但是,一旦出现非法字符,两者的行为就不一样了。
((log) => {
const tokenize = (TOKEN_REGEX, str) => {
const result = [];
let match;
while (match = TOKEN_REGEX.exec(str)) {
result.push(match[1]);
}
return result;
}
const TOKEN_Y = /\s*(\+|[0-9]+)\s*/y;
const TOKEN_G = /\s*(\+|[0-9]+)\s*/g;
log(tokenize(TOKEN_Y, '3x + 4'));
// [ '3' ]
log(tokenize(TOKEN_G, '3x + 4'));
// [ '3', '+', '4' ]
})(console.log)
上面代码中,g
修饰符会忽略非法字符,而y
修饰符不会,这样就很容易发现错误。
s
修饰符表示,特殊字符.
应另外匹配字符串中的下述行终结符(line terminator characters
),否则将会失配:
\n
)\r
)这实际上意味着.
将会匹配任意的单个Unicode Basic Multilingual Plane
(BMP
)字符。
若要使其与astral
字符(大于\uFFFF
的Unicode
字符)匹配,你应当使用u
(Unicode
)修饰符。
一起使用这两个修饰符
,.
将无一例外地匹配任意Unicode
字符。
((log) => {
log(/foo.bar/.test('foo\nbar')); // false
log(/foo.bar/s.test('foo\nbar')); // true
})(console.log)