讲给前端的正则表达式(3):使用 ES6 特性[每日前端夜话0x104]
前文:
到现在为止,我们已经介绍了许多正则表达式的功能。但是还有更多。这次我们将会学习一些更高级的概念,例如搜索和覆盖 JavaScript 中 RegExp 对象的更多功能。我们还将学习如何使用 ES6 带来的一些功能。开始吧!
exec
这是一种执行搜索字符串中的匹配项的方法(类似于 test 方法),但是它返回的结果是数组(或 null )。其结果还有其他一些属性,例如 index 和 input
1const string = 'fileName.png, fileName2.png, fileName3.png'; 2const regexp = /fileName[0-9]?.png/g; 3 4regexp.exec(string); 5 6[ 7 0: "fileName.png", 8 index: 0, 9 input: "fileName.png, fileName2.png, fileName3.png" 10]
index 是匹配项的位置, input 是提供的字符串。请注意,我在这里用的是 global 标志,在课程的第一部分中已提到过。所以我们可以通过多次调用 exec 在字符串中寻找多个匹配项。它将 RegExp 对象的 lastIndex 属性设置为一个数字,该数字指示搜索停止的位置。
1let resultArray; 2while((resultArray = regexp.exec(string)) !== null) { 3 console.log(resultArray[0], regexp.lastIndex); 4} 5 6// fileName.png 12 7// fileName2.png 27 8// fileName3.png 42
正则表达式中的分组
使用正则表达式,不仅可以检查字符串是否匹配,还可以在忽略不必要字符的同时提取某些信息。可以使用带有圆括号的分组。
1function getDateFromString(dateString) { 2 const regexp = /([0-9]{2})-([0-9]{2})-([0-9]{4})/; 3 const result = regexp.exec(dateString); 4 if(result) { 5 return { 6 day: result[1], 7 month: result[2], 8 year: result[3] 9 } 10 } 11} 12 13getDateFromString('14-05-2018');
1{ 2 day: '14', 3 month: '05', 4 year: '2018' 5}
在这种情况下,我们提取了三组字符,而忽略了破折号。只需注意 result [0]
将是匹配的完整字符串。
已经有一个处于第4阶段的命名组提案【 https://github.com/tc39/proposal-regexp-named-groups 】,并且在上述用例中被证明是有帮助的。Axel Rauschmayer 在 2ality 博客上的文章【 http://2ality.com/2017/05/regexp-named-capture-groups.html 】中对此进行了很好的描述。
嵌套分组
你可以嵌套分组:
1function getYearFromString(dateString) { 2 const regexp = /[0-9]{2}-[0-9]{2}-([0-9]{2}([0-9]{2}))/; 3 const result = regexp.exec(dateString); 4 if(result) { 5 return { 6 year: result[1], 7 yearShort: result[2] 8 } 9 } 10} 11 12getYearFromString('14-05-2018');
1{ 2 year: '2018', 3 yearShort: '18' 4}
在模式的 ([0-9]{2}([0-9]{2}))
部分中,我们将一组嵌套在另一组中。多亏了这一点,我们得到了表示年份的短字符串。
条件模式
还有另一个有用的功能,即 OR 语句。我们可以用 |
字符实现:
1function doYearsMatch(firstDateString, secondDateString) { 2 const execResult = /[0-9]{2}-[0-9]{2}-([0-9]{4})/.exec(firstDateString); 3 if(execResult) { 4 const year = execResult[1]; 5 const yearShort = year.substr(2,4); 6 return RegExp(`[0-9]{2}-[0-9]{2}-(${year}|${yearShort})`).test(secondDateString); 7 } 8} 9 10doYearsMatch('14-05-2018', '12-02-2018'); // true 11doYearsMatch('14-05-2018', '24-04-18'); // true
按照我们的模式, (${year}|${yearShort})
将会匹配年份,即使第二个年份以简短形式提供。
全部捕获
与组一起工作时,还有一个特别有用的地方: (. *)
1function getResolution(resolutionString) { 2 const execResult = /(.*) ?x ?(.*)/.exec(resolutionString); 3 if(execResult) { 4 return { 5 width: execResult[1], 6 height: execResult[2] 7 } 8 } 9}
1getResolution('1024x768'); 2{ 3 width: '1024', 4 height: '768' 5}
多亏用了 ?
运算符,如果还有其他空格,它也将起作用:
1getResolution('1920 x 1080'); 2 3{ 4 width: '1920', 5 height: '1080' 6}
粘性标志(sticky)
如你所见,RegExp 对象有一个名为 lastIndex 的属性。当进行全局搜索(使用适当的标志)时,可以在正确的位置继续进行模式匹配。使用 ES6 中引入的 粘性标志 y
,我们可以强制从某个索引开始搜索。
1function getDateFromString(dateString) { 2 const regexp = /([0-9]{2})-([0-9]{2})-([0-9]{4})/y; 3 regexp.lastIndex = 14; 4 const result = regexp.exec(dateString); 5 if(result){ 6 return { 7 day: result[1], 8 month: result[2], 9 year: result[3] 10 } 11 } 12} 13 14getDateFromString('Current date: 14-05-2018');
请记住,对字符串执行检查(例如使用 exec )会更改 lastIndex 属性,所以如果你希望它在多次粘性搜索之间保持不变,请不要忘记对其进行设置。如果模式匹配失败,则将 lastIndex 设置为0。
注意:你可以检查 RegExp 对象是否启用了标志。
1const regexp = /([0-9]{2})-([0-9]{2})-([0-9]{4})/y; 2regexp.lastIndex = 14; 3console.log(regexp.sticky); // true
其他标志也是如此:更多相关的标志,请访问 MDN Web 文档【 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp#RegExp_prototype_objects_and_instances 】。
Unicode 标志
ES6 也带来了对 Unicode 的更好支持。添加 Unicode 标志 u
可以启用与 Unicode 相关的其他功能。多亏了它,你可以在模式中使用 \u{x}
,其中 x 是所需字符的编码。
1/\u{24}/u.test('$'); // true
以上代码如果没有 u
标志将会无法工作。重要的是要知道它的影响不仅限于此。如果不带标志,也可以处理一些的更特殊的 Unicode 字符:
1/:joy_cat:/.test(':joy_cat:'); // true
但这会在更复杂的情况下使我们失败:
1/a.b/.test('a:joy_cat:b'); // false 2/a.b/u.test('a:joy_cat:b'); // true 3 4/:joy_cat:{2}/.test(':joy_cat::joy_cat:'); // false 5/:joy_cat:{2}/u.test(':joy_cat::joy_cat:'); // true 6 7/[a:joy_cat:b]/.test(':joy_cat:'); // false 8/[:joy_cat::dog:]/u.test(':joy_cat:'); // true 9 10/^[^x]$/.test(':joy_cat:'); // false 11/[^x]/.test(':joy_cat:'); // true 12/^[^x]$/u.test(':joy_cat:'); // true 13 14/^[ab:joy_cat:]$/.test(':joy_cat:'); // false 15/[ab:joy_cat:]/.test(':joy_cat:'); // true 16/^[ab:joy_cat:]$/u.test(':joy_cat:'); // true
我们可以很容易地得出一个结论,那就是在模式中包含 u
标志是一种很好的做法,尤其是再可能含有除了标准 ASCII 之外的其他字符的情况。
如果将它与 忽略大小写 标志结合使用,则该模式也将同时匹配小写和大写字符。
1/\u{78}/ui.test('X'); // true
有趣的是,在 HTML 中 input 和 textarea 元素的 pattern 属性中,默认情况下启用此标志。
总结
今天,我们了解了有关 JavaScript 中的 RegExp 对象的更多信息,以及如何通过正则表达式的一个强大功能来运用这个知识:分组。我们还学习了两个新标记:粘性和 Unicode。下次再见!
原文: https://wanago.io/2018/05/14/regex-course-part-three-grouping-and-using-es6-features/