Skip to content

正则表达式

基本匹配

javascript
/2020/.test('2020') // true

修饰符

  • g 全局搜索
  • i 不区分大小写
  • m 多行匹配
javascript
"/The/gi" => The fat cat sat on the mat.

字符组

  • [ ] 字符类,匹配方括号中包含的任意字符。
  • [^ ] 否定字符类。匹配方括号中不包含的任意字符
javascript
/[Jj]ava/.test('java') // true
/[Jj]ava/.test('Java') // true

字符区间

img

转义

特殊符号需要转移 \

快捷字符

  • \d:等于[0-9]
  • \D:和上面相反
  • \w:等于[a-zA-Z0-9_],注意下划线
  • \W:和上面相反
  • \s:匹配空白符(比如空格,tab、换行等)
  • \S:匹配非空白字符
  • .:任意字符

量词

  • ? : 0或1
  • * :>=0 等于{0,}
  • + : >=1 等于{1,}
  • {m,n}:出现次数(m <= x<= n)

位置

  • ^:匹配头
  • $:匹配尾
javascript
console.log('hello'.replace(/^/, '---'))   //---hello

console.log('hello'.replace(/$/, '---'))   //hello---
  • \b:单词边界
  • \w和\W之间的位置
  • ^与\w之间的位置
  • \w与$之间的位置

img

javascript
'xxx_love_study_1.mp4'.replace(/\b/g, '❤️') // ❤️xxx_love_study_1❤️.❤️mp4❤️
  • \B:非单词的边界,与\b相反
  • \w与\w之间的位置
  • \W与\W之间的位置
  • ^与\W之间的位置
  • \W与$之间的位置

img

javascript
'[[xxx_love_study_1.mp4]]'.replace(/\B/g, '❤️')

//❤️[❤️[x❤️x❤️x❤️_❤️l❤️o❤️v❤️e❤️_❤️s❤️t❤️u❤️d❤️y❤️_❤️1.m❤️p❤️4]❤️]❤️

分组

分组引用

javascript
/*
提取年月日
2021-08-14
*/

let reg = /(\d{4})-(\d{2})-(\d{2})/

console.log('2021-08-14'.match(reg))
//  ["2021-08-14", "2021", "08", "14", index: 0, input: "2021-08-14", groups: undefined]

// 第二种解法,通过全局的$1...$9读取 引用的括号数据
let reg = /(\d{4})-(\d{2})-(\d{2})/
let string = '2021-08-14'

reg.test(string)

console.log(RegExp.$1) // 2021
console.log(RegExp.$2) // 08
console.log(RegExp.$3) // 14

分组回溯引用

除了通过js引用分组的内容,也可以通过正则来引用分组内容

javascript
let regex = /(\w+).*?</\1>/g

let string1 = "<div></div>";
let string2 = "<div></p>";

console.log( regex.test(string1) ); // true
console.log( regex.test(string2) ); // false

非捕获性分组

有时候,我们并不需要捕获某个分组的内容,但是又想使用分组的特性。

非捕获性括号 (?:表达式)

javascript
// 非捕获型引用
let reg = /(?:ab)+/g
console.log('ababa abbb ababab'.match(reg)) // ["abab", "ab", "ababab"]

或者

使用分组的同时还可以使用 或者(or)条件。

断言

?=正先行断言-存在
?!负先行断言-排除
?<=正后发断言-存在
?<!负后发断言-排除
javascript
'xxx_love_study_1.mp4'.replace(/(?=xxx)/g, '❤️') // ❤️xxx_love_study_1.mp4

'xxx_love_study_1.mp4'.replace(/(?!xxx)/g, '❤️') 
//x❤️x❤️x❤️_❤️l❤️o❤️v❤️e❤️_❤️s❤️t❤️u❤️d❤️y❤️_❤️1❤️.❤️m❤️p❤️4❤️

'xxx_love_study_1.mp4'.replace(/(?<=xxx)/g, '❤️') //xxx❤️_love_study_1.mp4

'xxx_love_study_1.mp4'.replace(/(?<!xxx)/g, '❤️') 
//❤️x❤️x❤️x_❤️l❤️o❤️v❤️e❤️_❤️s❤️t❤️u❤️d❤️y❤️_❤️1❤️.❤️m❤️p❤️4❤️

样例

数字的千分位分割法

javascript
let price = '123456789'
let priceReg = /(?!^)(?=(\d{3})+$)/g

console.log(price.replace(priceReg, ',')) // 123,456,789

手机号3-4-4分割

javascript
let mobile = '18379836654'
let mobileReg = /(?=(\d{4})+$)/g

console.log(mobile.replace(mobileReg, '-')) // 183-7983-6654

案例

匹配id

javascript
// 1
let regex = /id=".*?"/ // 想想为什么要加? 不加的话 连后面的class都会匹配到
let string = '<div id="container" class="main"></div>';
console.log(string.match(regex)[0]);
// 2
let regex = /id="[^"]*"/ 
let string = '<div id="container" class="main"></div>'; 
console.log(string.match(regex)[0]);

匹配颜色

javascript
// 要求匹配如下颜色
/*
#ffbbad
#Fc01DF
#FFF
#ffE
*/

let regex = /#([a-fA-F\d]{6}|[a-fA-F\d]{3})/g
let string = "#ffbbad #Fc01DF #FFF #ffE";

console.log(string.match(regex))
//  ["#ffbbad", "#Fc01DF", "#FFF", "#ffE"]

匹配时间

javascript
/*
    要求匹配
  23:59
  02:07
*/
// 解析:
// 第一位:可以是0、1、2
// 第二位:当第一位位0或者1的时候,可以是0到9、第一位是2的时候,只可以是0到3
// 第三位:固定是冒号:
// 第四位:可以是0到5
// 第五位:0到9

let regex = /^([01]\d|2[0-3]):[0-5]\d$/

console.log(regex.test('23:59')) // true
console.log(regex.test('02:07'))// true

// 衍生题,可以是非0
let regex = /^(0?\d|1\d|2[0-3]):(0?|[1-5])\d/

console.log( regex.test("23:59") ) // true
console.log( regex.test("02:07") ) // true
console.log( regex.test("7:09") ) // true

匹配日期

javascript
/*
    要求匹配
  23:59
  02:07
*/
// 解析:
// 第一位:可以是0、1、2
// 第二位:当第一位位0或者1的时候,可以是0到9、第一位是2的时候,只可以是0到3
// 第三位:固定是冒号:
// 第四位:可以是0到5
// 第五位:0到9

let regex = /^([01]\d|2[0-3]):[0-5]\d$/

console.log(regex.test('23:59')) // true
console.log(regex.test('02:07'))// true

// 衍生题,可以是非0
let regex = /^(0?\d|1\d|2[0-3]):(0?|[1-5])\d/

console.log( regex.test("23:59") ) // true
console.log( regex.test("02:07") ) // true
console.log( regex.test("7:09") ) // true

懒惰匹配

正则表达式通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。比如这个表达式:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab,这被称为贪婪匹配

懒惰匹配,也就是匹配尽可能少的字符。在能使整个匹配成功的前提下使用最少的重复,只要在它后面加上一个问号?即可。现在看看懒惰版的例子吧:

a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)

javascript
*? 
+?
??
{n,m}?
{n,}?

RegExp对象

定义

javascript
var regex = /xyz/; //字面量定义和下面的相等,该定义方式更高效

var regex = new RegExp('xyz');

斜杠 "/" 会告诉 JavaScript 我们正在创建一个正则表达式。它的作用类似于字符串的引号。new RegExp 可以动态传入参数创建正则。

实例方法

test()

test方法返回一个布尔值,表示当前模式是否能匹配参数字符串

javascript
/cat/.test('cats and dogs') // true

exec()

exec()方法,用来返回匹配结果。如果发现匹配,就返回一个数组,成员是匹配成功的子字符串,否则返回null。

javascript
var s = '_x_x';
var r1 = /x/;
var r2 = /y/;

r1.exec(s) // ["x"]
r2.exec(s) // null

String实例方法

match()

match()方法的唯一参数就是一个正则表达式,返回的是一个由匹配结果组成的数组。如果该正则表达式设置了修饰符g,则该方法返回的数组包含字符串的所有匹配结果。

javascript
"1 plus 2 equals 3".match(/\d+/g)
// 返回 ["1","2","3"]

如果没有设置修饰符g,那么就不会进行全局检索,只检索第一个匹配。但是即使match()执行的不是全局检索,也返回一个数组。这时,数组的第一个元素就是匹配的字符串,余下的元素则是正则表达式中用圆括号括起来的子表达式。如果没有,则返回null。

javascript
"1 plus 2 equals 3".match(/\d+/)
// [ '1', index: 0, input: '1 plus 2 equals 3', groups: undefined ]

search()的参数是一个正则表达式,返回第一个与之匹配的子串的起始位置,如果找不到匹配的子串,它将返回-1。

javascript
"JavaScript".search(/script/i);

如果search()的参数不是正则表达式,首先会通过 RegExp 构造函数将它转换为正则表达式,search()方法不支持全局检索,因为会忽略正则表达式参数中的修饰符g。

replace()

该方法用以执行检索和替换操作。其中第一个参数是一个正则表达式,第二个参数是要进行替换的字符串。

如果正则表达式中设置了修饰符 g,那么源字符串中所有与模式匹配的子串都将替换成第二个参数指定的字符串;如果不带修饰符g,则只替换所匹配的第一个子串。

如果 replace() 的第一个参数是字符串而不是正则表达式,那么replace()将直接搜索这个字符串,而不会向search()一样先转换为正则表达式。

javascript
str.replace(/javascript/gi,"JavaScript");
//将所有不区分大小写的javascript替换成JavaScript

yyyy-mm-dd 格式,替换成 mm/dd/yyyy

javascript
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
var result = string.replace(regex, "$2/$3/$1");
console.log(result)

使用函数

javascript
function replacer(match, p1, p2, p3, offset, string) {
  console.log(match); //abc12345#$*%
  return [p1, p2, p3].join(' - ');
}
var newString = 'abc12345#$*%'.replace(/([^\d]*)(\d*)([^\w]*)/, replacer);
console.log(newString);  // abc - 12345 - #$*%

参考

工具

https://regexper.com/

ttps://regexr-cn.com/

https://jex.im/regulex/

https://regex101.com/

正则案例

字符串 trim 方法模拟

trim 方法是去掉字符串的开头和结尾的空白符

javascript
function trim(str) {
	return str.replace(/^\s+|\s+$/g, ''); // 替换开头和结尾的空白符
}

function trim (str) {
	return str.replace(/^\s*(.*?)\s*$/g, "$1");// 匹配整个字符串,然后用引用来提取出相应的数据
}
//这里使用了惰性匹配 *?,不然也会匹配最后一个空格之前的所有空格的。

将每个单词的首字母转换为大写

javascript
function titleize(str) {
   /\b\w/g 也可
  return str.replace(/(^|\s)\w/g, function (c) {
    return c.toUpperCase();
  });
}

驼峰化

javascript
function camelize(s) {
    return s.replace(/-\w/g, function(x) {
        return x.slice(1).toUpperCase();
    })
}

中划线化

javascript
function dasherize (str) {
return str.replace(/([A-Z])/g, '-$1').replace(/[-_\s]+/g, '-').toLowerCase();
}