一文搞懂js正则表达式

1,186 阅读4分钟

很多时候,我们碰到需要使用正则对字符串进行处理都是直接搜索,一直没有系统地去学习正则表达式。最近花了一些时间学习了一下正则表达式,总结一篇文章和大家分享。

记住:正则匹配=字符匹配+位置匹配。


1. 字符匹配

1.1 横向模糊匹配

横向模糊匹配意思就是这个字符可能会连续出现多次。方法是使用量词{a,b}。
比如:/ab{2,4}c/g 这个正则的意思就是abbc、abbbc、abbbbc都是满足该匹配规则的。

例子

var regex = /ab{2,4}c/g;
var str = 'abc abbc abbbc abbbbc abbbbbc abbbbbbc';
console.log(str.match(regex));  // [ 'abbc', 'abbbc', 'abbbbc' ]
1.1.1 量词
  • 简写形式

正则对于常见的量词,内置了如下5种简写形式:

  • 贪婪匹配

记住:量词默认是使用贪婪模式去匹配的,也就是会尽可能多地去匹配。

例子

var regex = /\d{2,5}/g;
var str = '123 1234 12345 123456';
console.log(str.match(regex));  // [ '123', '1234', '12345', '12345' ]
// 记忆:横向模糊匹配使用量词->蛮横->贪婪。
  • 惰性匹配

有些时候我们不希望使用贪婪模式去匹配,这个时候就要使用惰性匹配的方式了。
就是在量词后面加一个?。记忆:你知足了吗?

例子

var regex1 = /\d{2,5}?/g;
var str = '123 1234 12345 123456';
console.log(str.match(regex));
// ["12", "12", "34", "12", "34", "12", "34", "56"]
// 可以看出,此时是尽可能少地去匹配,也就是匹配2个数字就满足了。

1.2 纵向模糊匹配

纵向模糊匹配意思就是这个字符不确定,有多种情况。方法是使用字符组。
比如: /a[123]b/g 这个正则的意思就是a1b、a2b、a3b都是满足该匹配规则的。

例子

var regex = /a[123]b/g;
var str = 'a0b a1b a2b a3b a4b';
console.log(str.match(regex));  // [ 'a1b', 'a2b', 'a3b' ]
1.2.1 字符组
  • 范围表示法

对于很长的字符组,如果有规律的话,我们就可以使用范围表示法来表示:
比如: [12345abcdeABCDE] 这中连续的字符组可以表示成:[1-5a-eA-E]。

  • 排除字符组

如果想要表示一个除a、b、c之外的任意字符,可以在字符组的第一位写^(脱字符),即[^abc]

  • 常见的字符组简写形式

对于常见的字符组,正则内置了7种常见的简写形式:

1.3 多选分支

字符组是对单个字符取或,如果想对多个字符拼成的像单词这样的形式取或的话就要使用到多选分支。方法:xxx|yyy。

例子

var regex = /good|nice/g;
var str = 'good idea, nice try';
console.log(str.match(regex));  //  ['good', 'nice' ]

注意:多选分支是惰性匹配!!!

var regex = /good|goodbye/g;
var str = 'goodbye';
console.log(str.match(regex));  // ['good']
// 所以,如果想要优先匹配 goodbye ,需要把 goodbye 放到前面。

2. 位置匹配

位置(锚)是相邻字符之间的位置。

在ES5中,一共有6个锚:

2.1 ^$

^匹配每一行的开头
$匹配每一行的结尾

例子

var str = 'hello\nworld\ni\nam\nfine';
console.log(str.replace(/^|$/gm, '#'));  // m: 多行匹配
/* 
#hello#
#world#
#i#
#am#
#fine#
*/

2.2 \b\B

\b:单词边界。包括:单词和非单词之间、单词和开头之间、单词和结尾之间。
\B:非单词边界。除了\b的位置,别的都是\B

例子

console.log('[apple] test.mp4'.replace(/\b/g, '#'));  // [#apple#] #test#.#mp4#
console.log('[apple] test.mp4'.replace(/\B/g, '#'));  // #[a#p#p#l#e]# t#e#s#t.m#p#4

2.3 (?=p)(?!p)

(?=p):正向先行断言。即该位置后面的字符要匹配p。
(?!p):负向先行断言。即该位置后面的字符不能匹配p。

例子

console.log('hello'.replace(/(?=l)/g, '#'));  // he#l#lo
console.log('hello'.replace(/(?!l)/g, '#'));  // #h#ell#o#

掌握到这,我们就能够使用正则处理日常大部分正则工作了。


3. 正则表达式的括号

正则引擎在匹配过程中,给每一个分组都会开辟一个空间,用来存储每一个分组匹配到的数据。括号的作用其实就是提供了分组,便于我们引用它。

3.1 提取数据的3种方法

  • 字符串的match方法
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var str = '2020-09-20';
console.log(str.match(regex));
/* 
[
  '2020-09-20',
  '2020',  // 组
  '09',  // 组
  '20',  // 组
  index: 0,
  input: '2020-09-20',
  groups: undefined
]
*/
  • 正则对象的exec方法
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var str = '2020-09-20';
console.log(regex.exec(str));
/* 
[
  '2020-09-20',
  '2020',  // 组
  '09',  // 组
  '20',  // 组
  index: 0,
  input: '2020-09-20',
  groups: undefined
]
*/
  • 通过构造函数RegExp的全局属性$1$9获取
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var str = '2020-09-20';
// 获取之前必须先进行正则操作,例如:
// regex.test(str);
// regex.exec(str);
str.match(regex);
console.log(RegExp.$1);  // 2020
console.log(RegExp.$2);  // 09
console.log(RegExp.$3);  // 20

3.2 使用正则进行字符串替换的3种方法

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var str = '2020-09-20';
// 方式一
console.log(str.replace(regex, '$2/$3/$1'));  // 09/20/2020
// 方式二
console.log(str.replace(regex, function() {
    return RegExp.$2 + '/' + RegExp.$3 + '/' + RegExp.$1;
}));
// 方式三
console.log(str.replace(regex, function(match, year, month, day) {
    console.log(match);  // 2020-09-20
    return month + '/' + day + '/' + year;
}));

3.3 反向引用(\1\2、...)

在正则里面想要获取分组可以使用反向引用。
场景:比如匹配2020-10-08这个字符串,想要分割符前后一致,就需要用到反向引用。

var str1 = '2020-09-20';
var str2 = '2020/09/20';
var str3 = '2020.09.20';
var str4 = '2020-09/20';
var regex = /\d{4}(-|\/|\.)\d{2}\1\d{2}/;
console.log(regex.test(str1));  // true
console.log(regex.test(str2));  // true
console.log(regex.test(str3));  // true
console.log(regex.test(str4));  // false

3.4 非捕获括号(?:p)(?:p1|p2|p3)

如果不想让括号里面的数据被捕获(即本节开头说的分配存储空间),可以使用非捕获括号。非捕获括号也能起到性能优化的作用,节省了内存。

var regex = /\d(?:\w)/g;
var str = '1a';
console.log(regex.test(str));  // true
console.log(RegExp.$1);  // ''

本文主要是总结正则常用的语法,满足我们日常开发需要。对于像回溯这些高级内容感兴趣的可以自行了解。