@@ -291,9 +291,9 @@ ES6 为正则表达式新增了`flags`属性,会返回正则表达式的修饰
291
291
292
292
## s 修饰符:dotAll 模式
293
293
294
- 正则表达式中,点(` . ` )是一个特殊字符,代表任意的单个字符,但是行终止符 (line terminator character)除外 。
294
+ 正则表达式中,点(` . ` )是一个特殊字符,代表任意的单个字符,但是有两个例外。一个是四个字节的 UTF-16 字符,这个可以用 ` u ` 修饰符解决;另一个是行终止符 (line terminator character)。
295
295
296
- 以下四个字符属于”行终止符“。
296
+ 所谓行终止符,就是该字符表示一行的终结。 以下四个字符属于”行终止符“。
297
297
298
298
- U+000A 换行符(` \n ` )
299
299
- U+000D 回车符(` \r ` )
@@ -314,7 +314,7 @@ ES6 为正则表达式新增了`flags`属性,会返回正则表达式的修饰
314
314
// true
315
315
```
316
316
317
- 这种解决方案毕竟不太符合直觉,所以现在有一个 [ 提案 ] ( https://github.com/mathiasbynens/es -regexp-dotall-flag ) ,引入 ` / s` 修饰符,使得` . ` 可以匹配任意单个字符。
317
+ 这种解决方案毕竟不太符合直觉,ES2018 [ 引入 ] ( https://github.com/tc39/proposal -regexp-dotall-flag ) ` s ` 修饰符,使得` . ` 可以匹配任意单个字符。
318
318
319
319
``` javascript
320
320
/ foo. bar/ s .test (' foo\n bar' ) // true
@@ -336,7 +336,7 @@ re.flags // 's'
336
336
337
337
## 后行断言
338
338
339
- JavaScript 语言的正则表达式,只支持先行断言(lookahead)和先行否定断言(negative lookahead),不支持后行断言(lookbehind)和后行否定断言(negative lookbehind)。目前,有一个 [ 提案 ] ( https://github.com/goyakin/es -regexp-lookbehind ) ,引入后行断言, V8 引擎 4.9 版已经支持 。
339
+ JavaScript 语言的正则表达式,只支持先行断言(lookahead)和先行否定断言(negative lookahead),不支持后行断言(lookbehind)和后行否定断言(negative lookbehind)。ES2018 引入 [ 后行断言 ] ( https://github.com/tc39/proposal -regexp-lookbehind ) ,V8 引擎 4.9 版(Chrome 62)已经支持 。
340
340
341
341
”先行断言“指的是,` x ` 只有在` y ` 前面才匹配,必须写成` /x(?=y)/ ` 。比如,只匹配百分号之前的数字,要写成` /\d+(?=%)/ ` 。”先行否定断言“指的是,` x ` 只有不在` y ` 前面才匹配,必须写成` /x(?!y)/ ` 。比如,只匹配不在百分号之前的数字,要写成` /\d+(?!%)/ ` 。
342
342
@@ -375,9 +375,9 @@ const RE_DOLLAR_PREFIX = /(?<=\$)foo/g;
375
375
/ ^ (\d + )(\d + )$ / .exec (' 1053' ) // ["1053", "105", "3"]
376
376
```
377
377
378
- 上面代码中,需要捕捉两个组匹配。没有" 后行断言" 时,第一个括号是贪婪模式,第二个括号只能捕获一个字符,所以结果是` 105 ` 和` 3 ` 。而" 后行断言" 时,由于执行顺序是从右到左,第二个括号是贪婪模式,第一个括号只能捕获一个字符,所以结果是` 1 ` 和` 053 ` 。
378
+ 上面代码中,需要捕捉两个组匹配。没有“ 后行断言” 时,第一个括号是贪婪模式,第二个括号只能捕获一个字符,所以结果是` 105 ` 和` 3 ` 。而“ 后行断言” 时,由于执行顺序是从右到左,第二个括号是贪婪模式,第一个括号只能捕获一个字符,所以结果是` 1 ` 和` 053 ` 。
379
379
380
- 其次," 后行断言" 的反斜杠引用,也与通常的顺序相反,必须放在对应的那个括号之前。
380
+ 其次,“ 后行断言” 的反斜杠引用,也与通常的顺序相反,必须放在对应的那个括号之前。
381
381
382
382
``` javascript
383
383
/ (?<=(o)d\1 )r/ .exec (' hodor' ) // null
@@ -388,7 +388,7 @@ const RE_DOLLAR_PREFIX = /(?<=\$)foo/g;
388
388
389
389
## Unicode 属性类
390
390
391
- 目前,有一个 [ 提案 ] ( https://github.com/mathiasbynens/es -regexp-unicode-property-escapes ) ,引入了一种新的类的写法 ` \p{...} ` 和` \P{...} ` ,允许正则表达式匹配符合 Unicode 某种属性的所有字符。
391
+ ES2018 [ 引入 ] ( https://github.com/tc39/proposal -regexp-unicode-property-escapes ) 了一种新的类的写法 ` \p{...} ` 和` \P{...} ` ,允许正则表达式匹配符合 Unicode 某种属性的所有字符。
392
392
393
393
``` javascript
394
394
const regexGreekSymbol = / \p {Script=Greek}/ u ;
@@ -403,10 +403,11 @@ Unicode 属性类要指定属性名和属性值。
403
403
\p{UnicodePropertyName= UnicodePropertyValue}
404
404
```
405
405
406
- 对于某些属性,可以只写属性名。
406
+ 对于某些属性,可以只写属性名,或者只写属性值 。
407
407
408
408
``` javascript
409
409
\p{UnicodePropertyName}
410
+ \p{UnicodePropertyValue}
410
411
```
411
412
412
413
` \P{…} ` 是` \p{…} ` 的反向匹配,即匹配不满足条件的字符。
@@ -435,12 +436,18 @@ regex.test('ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ') // true
435
436
下面是其他一些例子。
436
437
437
438
``` javascript
439
+ // 匹配所有空格
440
+ \p{White_Space}
441
+
438
442
// 匹配各种文字的所有字母,等同于 Unicode 版的 \w
439
443
[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
440
444
441
445
// 匹配各种文字的所有非字母的字符,等同于 Unicode 版的 \W
442
446
[^ \p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
443
447
448
+ // 匹配 Emoji
449
+ / \p {Emoji_Modifier_Base}\p {Emoji_Modifier}? | \p {Emoji_Presentation}| \p {Emoji}\uFE0F / gu
450
+
444
451
// 匹配所有的箭头字符
445
452
const regexArrows = / ^ \p {Block=Arrows}+ $ / u ;
446
453
regexArrows .test (' ←↑→↓↔↕↖↗↘↙⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇧⇩' ) // true
@@ -459,15 +466,17 @@ const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;
459
466
上面代码中,正则表达式里面有三组圆括号。使用` exec ` 方法,就可以将这三组匹配结果提取出来。
460
467
461
468
``` javascript
469
+ const RE_DATE = / (\d {4} )-(\d {2} )-(\d {2} )/ ;
470
+
462
471
const matchObj = RE_DATE .exec (' 1999-12-31' );
463
472
const year = matchObj[1 ]; // 1999
464
473
const month = matchObj[2 ]; // 12
465
474
const day = matchObj[3 ]; // 31
466
475
```
467
476
468
- 组匹配的一个问题是,每一组的匹配含义不容易看出来,而且只能用数字序号引用 ,要是组的顺序变了,引用的时候就必须修改序号。
477
+ 组匹配的一个问题是,每一组的匹配含义不容易看出来,而且只能用数字序号(比如 ` matchObj[1] ` )引用 ,要是组的顺序变了,引用的时候就必须修改序号。
469
478
470
- 现在有一个“具名组匹配”(Named Capture Groups)的 [ 提案 ] ( https://github.com/tc39/proposal-regexp-named-groups ) ,允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。
479
+ ES2018 引入了 [ 具名组匹配 ] ( https://github.com/tc39/proposal-regexp-named-groups ) (Named Capture Groups) ,允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。
471
480
472
481
``` javascript
473
482
const RE_DATE = / (?<year>\d {4} )-(?<month>\d {2} )-(?<day>\d {2} )/ ;
@@ -559,3 +568,53 @@ const RE_TWICE = /^(?<word>[a-z]+)!\k<word>!\1$/;
559
568
RE_TWICE .test (' abc!abc!abc' ) // true
560
569
RE_TWICE .test (' abc!abc!ab' ) // false
561
570
```
571
+
572
+ ## String.prototype.matchAll
573
+
574
+ 如果一个正则表达式在字符串里面有多个匹配,现在一般使用` g ` 修饰符或` y ` 修饰符,在循环里面逐一取出。
575
+
576
+ ``` javascript
577
+ var regex = / t(e)(st(\d ? ))/ g ;
578
+ var string = ' test1test2test3' ;
579
+
580
+ var matches = [];
581
+ var match;
582
+ while (match = regex .exec (string)) {
583
+ matches .push (match);
584
+ }
585
+
586
+ matches
587
+ // [
588
+ // ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"],
589
+ // ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"],
590
+ // ["test3", "e", "st3", "3", index: 10, input: "test1test2test3"]
591
+ // ]
592
+ ```
593
+
594
+ 上面代码中,` while ` 循环取出每一轮的正则匹配,一共三轮。
595
+
596
+ 目前有一个[ 提案] ( https://github.com/tc39/proposal-string-matchall ) ,增加了` String.prototype.matchAll ` 方法,可以一次性取出所有匹配。不过,它返回的是一个遍历器(Iterator),而不是数组。
597
+
598
+ ``` javascript
599
+ const string = ' test1test2test3' ;
600
+ const regex = / t(e)(st(\d ? ))/ g ;
601
+ for (const match of string .matchAll (regex)) {
602
+ console .log (match);
603
+ }
604
+ // ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"]
605
+ // ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"]
606
+ // ["test3", "e", "st3", "3", index: 10, input: "test1test2test3"]
607
+ ```
608
+
609
+ 上面代码中,由于` string.matchAll(regex) ` 返回的是遍历器,所以可以用` for...of ` 循环取出。相对于返回数组,返回遍历器的好处在于,如果匹配结果是一个很大的数组,那么遍历器比较节省资源。
610
+
611
+ 遍历器转为数组是非常简单的,使用` ... ` 运算符和` Array.from ` 方法就可以了。
612
+
613
+ ``` javascript
614
+ // 转为数组方法一
615
+ [... string .matchAll (regex)]
616
+
617
+ // 转为数组方法二
618
+ Array .from (string .matchAll (regex));
619
+ ```
620
+
0 commit comments