跳到主要内容

正则

一本小书,看完非常有收获。下载地址

基础概念

结构说明
字面量匹配一个具体字符,包括不用转义的和需要转义的。比如 a 匹配字符 “a”,又比如 \n 匹配换行符,又比如 . 匹配小数点
字符组匹配一个字符,可以是多种可能之一,比如 【0-9】,表示匹配一个数字。
也有 \d 的简写形式
另外还有反义字符组,表示可以是除了特定字符之外任何一个字符,比如【^0-9】,表示一个非数字字符,也有 \D 的简写形式
量词表示一个字符连续出现,比如 a{1,3} 表示 “a” 字符连续出现 3 次。另外还有常见的简写形式,比如 a+ 表示 “a” 字符连续出现至少一次
匹配一个位置,而不是字符。比如 ^ 匹配字符串的开头,又比如 \b 匹配单词边界,又比如 (?=\d) 表示数字前面的位置。
分组用括号表示一个整体,比如 (ab)+ ,表示 “ab” 两个字符连续出现多次,也可以使用非捕获分组 (?:ab)+。
分支多个子表达式多选一,比如 abc|bcd,表达式匹配 “abc” 或者 “bcd” 字符子串。反向引用,比如 \2,表示引用第 2 个分组
操作符描述操作符优先级
转义符\1
括号和方括号(...)、(?:...)、(?=...)、(?!...)、[...]2
量词限定符{m} {m, n} {m,} ? * +3
位置和序列^ $ \元字符一般字符4
管道符(竖杠)5

常用的字符组合简写形式

一般来说,大写和小写的区别是,前者表示非。

\w :word 数字字母下划线

\W :非数字字母下划线

\b 是单词边界,具体就是 \w 与\W 之间的位置,也包括 \w 与 ^ 之间的位置,和 \w 与 $ 之间的位置。

比如考察文件名 "[JS] Lesson_01.mp4" 中的 \b,如下:

var result = "[JS] Lesson_01.mp4".replace(/\b/g, "#");
console.log(result);
// => "[#JS#] #Lesson_01#.#mp4#"

模式匹配

(?=p),其中 p 是一个子模式,即 p 前面的位置,或者说,该位置后面的字符要匹配 p。

var result = "hello".replace(/(?=l)/g, "#");
console.log(result);
// => "he#l#lo"

而 (?!p) 就是 (?=p) 的反面意思,比如:

var result = "hello".replace(/(?!l)/g, "#");
console.log(result);
// => "#h#ell#o#"

二者的学名分别是 positive lookahead 和 negative lookahead。
中文翻译分别是正向先行断言和负向先行断言。

ES5 之后的版本,会支持 positive lookbehind 和 negative lookbehind。
具体是 (?<=p) 和 (?<!p)。

使用 () 表示 捕获分组,使用 (?:) 表示 非捕获分组

常用正则

  • 字符串 trim 方法模拟(第二种性能更好)
// 第一种
function trim(str) {
return str.replace(/^\s+|\s+$/g, "");
}

// 第二种
function trim(str) {
return str.replace(/^\s*(.*?)\s*$/g, "$1");
}
  • 将每个单词的首字母转换为大写
function titleize(str) {
return str.toLowerCase().replace(/(?:^|\s)\w/g, function (c) {
return c.toUpperCase();
});
}
  • 驼峰化
function camelize(str) {
return str.replace(/[-_\s]+(.)?/g, function (match, c) {
return c ? c.toUpperCase() : "";
});
}
  • 中划线化
function dasherize(str) {
return str
.replace(/([A-Z])/g, "-$1")
.replace(/[-_\s]+/g, "-")
.toLowerCase();
}
  • HTML 转义和反转义

转义:

function escapeHTML(str){
var escapeChars = {
'<': 'lt',
'>': 'gt',
'"': 'quot',
'&': 'amp',
'\': '#39'
};
return str.replace(new RegExp('[' + Object.keys(escapeChars).join('')+']','g'),function(match){
return '&' + escapeChars[match] + ';';
});
}
console.log( escapeHTML('<div>Blah blah blah</div>') );
// => "&lt;div&gt;Blah blah blah&lt;/div&gt";

反转义:

function unescapeHTML(str) {
var htmlEntities = {
nbsp: " ",
lt: "<",
gt: ">",
quot: '"',
amp: "&",
apos: "'",
};
return str.replace(/\&([^;]+);/g, function (match, key) {
if (key in htmlEntities) {
return htmlEntities[key];
}
return match;
});
}
console.log(unescapeHTML("&lt;div&gt;Blah blah blah&lt;/div&gt;"));
// => "<div>Blah blah blah</div>"
  • 匹配成对标签
var regex = /<([^>]+)>[\d\D]*<\/\1>/;
var string1 = "<title>regular expression</title>";
var string2 = "<p>laoyao bye bye</p>";
var string3 = "<title>wrong!</p>";
console.log(regex.test(string1)); // true
console.log(regex.test(string2)); // true
console.log(regex.test(string3)); // false

其他

贪婪量词:买衣服砍价。价格高了、便宜点、不行,再便宜点。 惰性量词:卖东西加价。给少了,再给点。 分支结构:货比三家。

要匹配字符串 "{3,5}",只需要把正则写成 /\{3,5}/ 即可

效率 tips:

  • 使用具体型字符组来代替通配符,来消除回溯
  • 使用非捕获型分组
  • 独立出确定分组
  • 提取分支公共部分
  • 减少分支的数量,缩小它们的范围