跳到主要内容

正则表达式进阶技巧

正向预查

正向预查可以让我们在匹配某个模式之前,先检查是否满足某个条件。常见的正向预查有两种:

?=* 匹配后面紧跟着 * 的数字。例如:

var str = '1231231231';
var reg = /1(?=2)/g;
console.log(str.match(reg)); // ['1', '1', '1']

上面的正则表达式会匹配后面紧跟着 21

?!* 匹配后面没有紧跟着 * 的数字。例如:

var str = '1231231231';
var reg = /1(?!2)/g;
console.log(str.match(reg)); // ['1']

上面的正则表达式会匹配后面没有紧跟着 21

贪婪模式与非贪婪模式

正则表达式在匹配时,默认是贪婪模式,即只要能匹配多的就不匹配少的。例如:

var str = 'abcd{{efg}}abcd{{xzy}}';
var reg = /{{.*}}/g;
console.log(str.match(reg)); // ['{{efg}}abcd{{xzy}}']

可以看到,. 会匹配尽可能多的字符。

如果想改为非贪婪模式,只需在 * 后面加 ? 即可。例如:

var str = 'abcd{{efg}}abcd{{xzy}}';
var reg = /{{.*?}}/g;
console.log(str.match(reg)); // ['{{efg}}', '{{xzy}}']

这样就只会匹配到最少的满足条件的字符了。

非贪婪模式下,有时候会出现意想不到的结果,比如:

var str = 'aaaaaa';
var reg = /\w??/g;
console.log(str.match(reg)); // ['', '', '', '', '', '', '']

可以看到,非贪婪模式下,? 会匹配 0 次或 1 次,这里全部匹配了 0 次。

replace 方法

replace 方法可以用来替换字符串中的某些字符。但它本身不具备全局匹配的能力,只能匹配一次。例如:

var str = 'JS1231231';
var str1 = str.replace('123', '++');
console.log(str1); // JS++1231

如果想实现全局替换,需要在正则表达式上添加 g 修饰符。例如:

var str = 'JS123123';
var reg = /123/g;
var str1 = str.replace(reg, '$+');
console.log(str1); // JS$+$+

案例:将 xxyy 变成 yyxx

可以利用反向引用和重复子表达式来实现:

var str = 'aabbccdd';
var reg = /(\w)\1(\w)\2/g;
var str1 = str.replace(reg, '$2$2$1$1');
console.log(str1); // bbaaddcc

也可以使用 replace 的回调函数形式:

var str = 'aabbccdd';
var reg = /(\w)\1(\w)\2/g;
var str1 = str.replace(reg, function ($, $1, $2) {
return $2 + $2 + $1 + $1;
});
console.log(str1); // bbaaddcc

案例:将短横线命名改为驼峰命名

var str = 'js-plus-plus';
var reg = /-(\w)/g;
var str1 = str.replace(reg, function ($, $1) {
return $1.toUpperCase();
});
console.log(str1); // jsPlusPlus

案例:将驼峰命名改为下划线命名

var str = 'jsPlusPlus';
var reg = /([A-Z])/g;
var str1 = str.replace(reg, function ($, $1) {
return '_' + $1.toLowerCase();
});
console.log(str1); // js_plus_plus

案例:将 xxyyzz 变成 XxYyZz

var str = 'xxyyzz';
var reg = /(\w)\1(\w)\2(\w)\3/g;
var str1 = str.replace(reg, function ($, $1, $2, $3) {
return $1.toUpperCase() + $1 + $2.toUpperCase() + $2 + $3.toUpperCase() + $3;
});
console.log(str1); // XxYyZz

案例:将 aabbcc 变成 a$b$c$

如果想在 replace 中插入 $ 符号,需要使用两个 $$:

var str = 'aabbcc';
var reg = /(\w)\1(\w)\2(\w)\3/g;
var str1 = str.replace(reg, '$1$$$2$$$3$$');
console.log(str1); // a$b$c$

如果要插入其他特殊符号,也是类似的,在符号前面加一个 $ 即可:

var str = 'aabbcc';
var reg = /(\w)\1(\w)\2(\w)\3/g;
var str1 = str.replace(reg, '$1$$*2$$*3$*');
console.log(str1); // a$*2$*3$*

转义特殊字符

在正则表达式中,有些字符有特殊含义,比如 \?+ 等。如果想匹配这些字符本身,需要进行转义。例如:

var str = 'aa\\bb\\cc';
var reg = /\\/g;
console.log(str.match(reg)); // ['\\', '\\']
var str = 'aa?bb+cc';
var reg = /\?|\+/g;
console.log(str.match(reg)); // ['?', '+']

字符串去重

如果字符串中每种字符的个数都相等,可以这样去重:

var str = 'aabbcc';
var reg = /(\w)\1(\w)\2(\w)\3/g;
var str1 = str.replace(reg, '$1$2$3');
console.log(str1); // abc

如果字符个数不等,可以这样:

var str = 'aabbcc';
var reg = /(\w)\1*/g;
var str1 = str.replace(reg, '$1');
console.log(str1); // abc

给数字添加千位分隔符

// 处理整数和小数
function formatNumber(num) {
return num
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
.replace(/^(-?\d+)(\.\d+)?$/, function (_, int, dec) {
return int.replace(/\B(?=(\d{3})+(?!\d))/g, ',') + (dec || '');
});
}

console.log(formatNumber(1234567.89)); // 1,234,567.89

双大括号替换

var str = "My name is {{name}}. I'm {{age}} years old";
var reg = /{{(.*?)}}/g;
var str1 = str.replace(reg, function (match, key) {
return {
name: 'John',
age: 30,
}[key];
});
console.log(str1); // My name is John. I'm 30 years old

制作简单的 HTML 模板引擎

<div class="article">
<script type="text/html" id="tpl">
<div class="wrap">
<h1>{{title}}</h1>
<p>{{content}}</p>
<div>作者: {{author}}</div>
</div>
</script>
</div>

<script>
var tpl = document.getElementById('tpl').innerHTML;
var data = {
title: '标题',
content: '内容',
author: '作者',
};
var reg = /{{(.*?)}}/g;
var html = tpl.replace(reg, function (match, key) {
return data[key];
});
document.querySelector('.article').innerHTML = html;
</script>