正则表达式

2019.12/JavaScript

怎么用

正则表达式用来搜索和替换字符串。

// 两种创建语法
re1 = new RegExp(pattern[, flags])
re2 = /pattern/[flags]

flags修饰符:i不区分大小写,g查找所有匹配项,m多行模式,u完整unicode支持,y粘滞模式。

只查找首个匹配项

  • str.search(reg) - 返回第一个匹配位置,未找到则返回-1
const str = 'I love JavaScript';
str.search(/love/i); // 返回第一个匹配位置2
  • str.match(reg) - 不使用g修饰符,以数组形式返回第一个匹配项,无匹配项则返回null
const str = 'I love JavaScript';
const result = str.match(/Java(Script)/);

result[0]; // 完全匹配JavaScript
result[1]; // 第一个分组Script
result.length; // 2
result.index; // 匹配位置7
result.input; // 源字符串str
  • regexp.test(str) - 判断是否有匹配,返回true/false
const str = 'I love JavaScript';
/love/i.test(str); // true,相当于:
str.search(/love/i) !== -1;
  • regexp.exec(str) - 使用g修饰符,并设置regexp.lastIndex,从指定位置查找
const str = 'I love JavaScript';
const regexp = /\w+/g;
regexp.lastIndex = 2;
const result = regexp.exec(str);

result[0]; // 完全匹配love
result.index; // 匹配位置2
result.input; // 源字符串str

查找所有匹配项

  • str.match(reg) - 使用g修饰符,得到匹配项数组,不含index和捕获分组
const str = 'I love JavaScript';
str.match(/Java(Script)/g); // ['JavaScript']
  • regexp.exec(str) - 在循环中使用g修饰符,获取所有匹配项完整信息
const str = 'I love JavaScript';
const regexp = /\w+/g;
let result;
while (result = regexp.exec(str)) {
console.log(`Found ${result[0]} at position ${result.index}`);
}

搜索并替换

  • str.replace(reg, str|func)
'12-34-56'.replace(/-/g, ':');

在替代字符串中使用特殊字符:

符号 作用
$$ $符号
$& 匹配项
$` 匹配项左边的内容
$' 匹配项右边的内容
$n 第n个括号的匹配项
$<name> 命名分组的匹配项
const str = 'Adele Adkins';
str.replace(/(\w+) (\w+)/i, '$2, $1');

拆分字符串

  • str.split(str|reg)
'12,34, 56, 78'.split(/,\s*/); // ['12', '34', '56', '78']

字符类匹配

通过方括号[]表示匹配它包含的任意字符;通过方括号内开头的^来否定,表示不包含;在方括号中使用连字符-表示字符范围。

字符 匹配
[...] 方括号内的任意字符
[^...] 不在方括号内的任意字符
. 除换行符之外的任意字符
\d 数字,等同于[0-9]
\D 非数字,等同于[^0-9]
\w 单词,等同于[a-zA-Z0-9]
\W 非单词,等同于[^a-zA-Z0-9]
\s Unicode空白符
\S 非Unicode空白符

重复匹配

字符 含义
{n,m} 至少n次,不超过m次
{n,} n次或更多次
{n} n次
? 0次或1次,等同于{0,1}
+ 1次或多次,等同于{1,}
* 0次或多次,等同于{0,}

上述重复默认为“贪婪”匹配,即尽可能多地匹配,在待匹配字符后跟随问号?可改为“非贪婪”匹配。

选择项

使用符号|分隔供选择的字符,例如/ab|cd|ef/可以匹配ab,也可以匹配cd或者ef。注意,选择项从左到右尝试匹配,如果左边已匹配,就忽略右边的,即便右边的匹配更好。

分组和引用

圆括号()有多种作用:

  1. 把单独的项组合成表达式,一块使用|、?、+、*等处理
  2. 在完整模式中定义子模式,当正则表达式和目标字符串成功匹配时,可以从目标字符串中提取出和圆括号中的子模式相匹配的部分
const result = 'class007'.match(/[a-z]+(\d+)/);
result && result.length > 1 && result[1]; // 007
  1. 在同一个正则表达式的后部,通过字符\加上数字编号,引用前面的子表达式(引用匹配文本)
/(['"])[^'"]*\1/.test('I love "JavaScript"'); // true

注,如果只是单纯分组,不需要提取或引用,应该使用(?:…),即在左括号后面跟上?:。

匹配位置

匹配字符之间的位置,而不是实际的字符。

字符 含义
^ 开头
$ 结尾
\b 单词边界,即\w和\W之间,或者\w和字符串开头或结尾之间
\B 非单词边界
(?=...) 零宽正向先行断言
(?!...) 零宽负向先行断言

零宽断言:需要捕获的内容前后必须是特定内容,但又不捕获这些特定内容。

转义

如果想要匹配在正则表达式中有特殊用途的元字符,必须对它们进行转义。

( ) [ ] { } \ / | ^ $ ? * + . = ! :