正则表达式笔记
两种模糊匹配
-
正则表达式之所以强大,是因为能实现模糊匹配。
-
而模糊匹配,有两个方向上的"模糊”。
横向模糊和纵向模糊;横向模糊是指,一个正则可匹配的字符串的长度不是固定的,可以是多种情况的;
var regex = /ab{2,5}c/g; var string = "abc abbc abbbc abbbbc abbbbbc abbbbbbc"; console.log(string.match(regex)); // => ["abbc", "abbbc", "abbbbc", "abbbbbc"]纵向匹配:一个正则匹配的字符串,具体到某一位字符时,他可以不是某个确定的字符,可以有多种可能,如[abc]
字符组 具体含义 \d 表示[0-9]。表示一位数字。digit(数字) \D 表示[^0-9]。表示除数字以外的任意字符 \w 表示[0-9a-zA-Z_]。表示数字、大小写字母和下划线 \W 表示[^0-9a-za-z_]非单词字符 \s 表示[ \t\v\n\r\f]。表示空白符,包括空格、水平制表符、垂直制表符、换行符、回车符、换页符 \S 表示[^ \t\v\n\r\f]。非空白符 . 表示[^\n\r\u2028\u2029]。通配符,表示几乎任意字符。换行符、回车符、行分隔符和段分割符除外 -
可以使用[\d\D][\w\w][\s\S]和[^]中的任何一个表示匹配任意字符;
贪婪匹配与惰性匹配
var regex = /\d{2,5}/g; var string = "123 1234 12345 123456"; console.log(string.match(regex)); // => ["123", "1234", "12345", "12345"]“ /\d{2,5}/g ”是贪婪的,它会尽可能多的匹配。你能给我 6 个,我就要 5 个。你能给我 3 个,我就要 3 个。反正只要在能力范围内,越多越好
var regex = /\d{2,5}/g; var string = "123 1234 12345 123456"; console.log(string.match(regex)); // => ["12", "12", "34", "12", "34", "12", "34", "56"]其中,“ /\d{2,5}?/ ”表示,虽然 2 到 5 次都行,当 2 个就够的时候,就不再往下尝试了。
惰性量词 贪婪量词 {m,n}? {m,n} {m,}? {m,} ?? ? +? + _? _ - 注意:分支结构也是惰性的/good|goodbye/去匹配“goodbye”字符串时,结果是[“good”];把正则改成/goodbye|good/就是["goodbye"]
【??(问题)正则表达式中的“#”表示什么意思】
例子
- 匹配 id:要求从
<div id="container" class="main"></div>提取出 id="container"; 可能最开始想到的方案如下:
var regex = /id=".*"/; var string = '<div id="container" class="main"></div>'; console.log(string.match(regex)[0]); //=>id="container" class="main"原因是'.'是通配符,本身就匹配双引号的,而量词*又是贪婪的,当遇到 container 后面的双引号时,是不会停下来,会继续匹配,直到遇到最后一个双引号为止。
一个解决方案:是使用惰性匹配:
var regex = /id=".*?"/; var string = '<div id="container" class="main"></div>'; console.log(string.match(regex)[0]); //=>id="container"这个方案存在的问题是,效率比较低;
正则表达式位置匹配
- 在 Es5 中,有 6 个锚:^,$,\b,\B,(?=p),(?!p);
^(脱字符)匹配开头,在多行匹配中匹配行开头;$(美元符号)匹配结尾,在多行匹配中匹配行结尾;\b是单词边界,具体就是\w(数字 0-9 大小写字母下划线) 与\W 之间的位置,也包括\w与^(开头)之间的位置,和\w与\$(结尾)之间的位置\B是非单词边界;应理解为(非单词)边界,而不是非(单词边界),'它仍然匹配的是边界';正则中所说的单词指的是\w可以匹配的字符,即数字、大小写以及下划线[0-9a-zA-Z].(?=p)其中 p 是一个子模式,即 p 前面的位置,或者说,该位置后面的字符要匹配 p。比如(?=l),表示“l"字符前面的位置,例如:
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#"
- 总结:
(?=p)和(?!p)分别就是正向先行断言和负向先行断言。(?<=p)和(?<!p)环视,即看看左边,看看右边;(?=p)和(?!p),(?<=p)和(?<!p)这 4 个就是匹配“位置”(?=p)就是 p 前面的那个位置
位置的特性
-
对于位置的理解,我们可以理解成空字符“”。
“hello” == ""+"h"+""+"e"+""+"l"+""+"l"+""+"o"+"";也等价于:“hello” ==""+""+"hello";
- 边界:正则中的位置分为"字符的占位" 和"字符的间隙";字符的占位是显示的位置;以 I'm iron man 为例,肉眼可见的字母 符号 空格都是可以占位的字符,也就是可以用下表获取到的位置。 字符的间隙是隐式的位置,即显示位置之间的位置,比如 I 和'之间的位置,字符串开头和 I 之间的位置等。
边界:指的是占位的字符左右的间隙位置
- 边界:正则中的位置分为"字符的占位" 和"字符的间隙";字符的占位是显示的位置;以 I'm iron man 为例,肉眼可见的字母 符号 空格都是可以占位的字符,也就是可以用下表获取到的位置。 字符的间隙是隐式的位置,即显示位置之间的位置,比如 I 和'之间的位置,字符串开头和 I 之间的位置等。
-
\b单词边界:单词边界匹配的就是这样的间隙位置:左边占位的字符或右边占位的字符,至少有一个不是\w
// 只有收尾位置匹配
console.log("0aZ_".replace(/\b/g, ".")); //.0aZ_.
// +不是\w,所以它的左右间隙都可以被匹配
console.log("a+a".replace(/\b/g, ".")); //.a.+.a.
// 空格也不是\w,所以它的左右间隙都可以被匹配
console.log("a a".replace(/\b/g, ".")); //.a. .a.
\B非单词 边界:它匹配的也是变价,针对的是与\b相反的非单词(\w),也就是说所有不能被\b匹配的边界
// 只有收尾位置匹配
console.log("0aZ_".replace(/\b/g, ".")); //0.a.Z._
// +不是\w,所以它的左右间隙都可以被匹配
console.log("a+a".replace(/\b/g, ".")); //a+a
// 空格也不是\w,所以它的左右间隙都可以被匹配
console.log("a a".replace(/\b/g, ".")); //a a
- 不匹配任何东西的正则: /.^/
正则表达式括号的作用
- 括号的作用:提供了分组,便于引用它;
- 引用某个分组,会有两种情况:在 JS 里引用它,在正则表达式里引用它。
(?:P)非捕获匹配
//将每个单词的首字母转换为大写
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();
}
// 将驼峰转换成连字符
function (str) {
return str.replace(hyphenateRE, '-$1').toLowerCase()
}
//str.replace(/([A-Z])/g,'-$1')表示在匹配到的大写字母前加上中划线
//将HTML特殊字符转换成等值的实体
function escapeHTML(str) {
var escapeChars = {
"<": "It",
">": "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>"));
// => "<div>Blah blah blah</div>";
// 实体字符转换为等值的HTML。
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("<div>Blah blah blah</div>"));
// => "<div>Blah blah blah</div>"
正则表达式回溯法原理
- 回溯法也称试探法,其基本思想是:从问题的某一状态(初始状态)出发,搜索从这种状态出发所能达到的所有“状态”,当一条路走到“尽头”的时候(不能再前进),再后退一步或若干步,从另外一种可能“状态”出发,继续搜索,直到所有的“路径”(状态)都试探过。这种不断“前进”,不断“回溯”的方法,就称作“回溯法”
- 出现的情况:
- 贪婪量词
- 惰性量词
- 分支结构
- JS 的正则引擎是 NFA(非确定型有限自动机)
- 字符字面量、字符组、量词、锚、分组、选择分支、反向引用。
例子
var utils = {};
"Boolean|Number|String|Function|Array|Data|RegExp|Object|Error"
.split("|")
.forEach(function(item) {
utils["is" + item] = function(obj) {
return {}.toString.call(obj) == "[object" + item + "]";
};
});
console.log(utils.isArray([1, 2, 3]));
// => true
相关 API 注意要点
- 用于正则操作的方法共有 6 个,字符串实例 4 个,正则实例 2 个;
- String.search(regexp);
- String.split([separator[,limit]])separator 可以是一个字符串或正则表达式
- String.match(regexp)
- String.replace(regexp|substr,newSubStr|function)
- RegExp.test(str)
- RegExp.exec(str) 如果匹配失败,exec() 方法返回 null,并将 lastIndex 重置为 0 。
- 字符串实例的 4 个方法都支持正则和字符串,但 search 和 match,会把字符串转换为正则的;
- exec 比 match 更强大;当正则没有 g 时,使用 match 返回的信息比较多,但是有 g 后,就没有关键的信息 index;而 exec 方法就能解决该问题,因为它能接着上一次匹配后继续匹配;
- 正则实例的两个方法 test,exec,当正则是全局匹配时,每一次匹配完成后,都会修改 lastIndex。
replace有两种使用形式,这是由于其第二个参数可以是字符串,也可以是函数;当第二个蚕食是字符串时,如下的字符有特殊的含义:属性 描述 2,…,$99 匹配第 1-99 个 分组里捕获的文本 $& 匹配到的子串文本 $` 匹配到的子串的左边文本 $' 匹配到的子串的右边文本 $$ 美元符号
正则构造函数属性
构造函数的静态属性基于所执行的最近一次正则操作而变化
| 静态属性 | 描述简写形式 |
|---|---|
| RegExp.input | 最近一次目标字符串 RegExp["$_"] |
| RegExp.lastMatch | 最近一次匹配的文本 RegExp["$&"] |
| RegExp.lastParen | 最近一次捕获的文本 RegExp["$+"] |
| RegExp.leftContext | 目标字符串中 lastMatch 之前的文本 RegExp["$`"] |
| RegExp.rightContext | 目标字符串中 lastMatch 之后的文本 RegExp["$'"] |
例子:正则表达式的理解/(?:^|/).?.$/[该正则表达式匹配的是“..”或“/..”]
其中:
\表示转义字符;.:匹配的是任意字符;\.:表示匹配的是.;?:表示匹配0个或1个;$:表示结尾;?::表示非匹配分组;():表示用来分组;^:在这里表示空格;/^|\//:表示匹配一个/|:表示或
总结
- 非常幸运能看到这篇讲解正则表达式的迷你书,现在能看懂正则表达式了,开心