一、字符匹配攻略
- 横向模糊匹配(贪婪模式)
- 纵向模糊匹配 [ ]
- 字符组 [ ] : 匹配一个字符
- 范围表示法:比如 [123456abcdefGHIJKLM],可以写成 [1-6a-fG-M]。用连字符 - 来省略和简写
- 排除字符组 :例如 [^abc],表示是一个除 "a"、"b"、"c"之外的任意一个字 符。字符组的第一位放 ^(脱字符),表示求反的概念。
- 常见的简写形式
字符组 | 具体含义 |
---|---|
\d | 表示 [0-9]。表示是一位数字。 记忆方式:其英文是 digit(数字)。 |
\D | 表示 [^0-9]。表示除数字外的任意字符。本 |
\w | 表示 [0-9a-zA-Z_]。表示数字、大小写字母和下划线。 记忆方式:w 是 word 的简写,也称单词字符。 |
\W | 表示 [^0-9a-zA-Z_]。非单词字符。 |
\s | 表示 [ \t\v\n\r\f]。表示空白符,包括空格、水平制表符、垂直制表符、换行符、回车符、换页符。记忆方式:s 是 space 的首字母,空白符的单词是 white space |
\S | 表示 [^ \t\v\n\r\f]。 非空白符。 |
. | 表示 [^\n\r\u2028\u2029]。通配符,表示几乎任意字符。换行符、回车符、行分隔符和段分隔符 除外。记忆方式:想想省略号 ... 中的每个点,都可以理解成占位符,表示任何类似的东西。 |
- 常见的量词简写模式
量词 | 具体含义 |
---|---|
{m,} | 表示至少出现 m 次。 |
{m} | 等价于 {m,m},表示出现 m 次。 |
? | 等价于 {0,1},表示出现或者不出现。 记忆方式:问号的意思表示,有吗? |
+ | 等价于 {1,},表示出现至少一次。 记忆方式:加号是追加的意思,得先有一个,然后才考虑追加。 |
* | 等价于 {0,},表示出现任意次,有可能不出现。 记忆方式:看看天上的星星,可能一颗没有,可能零散有几颗,可能数也数不过来。 |
- 贪婪匹配与惰性匹配
通过在量词后面加个问号就能实现惰性匹配
惰性量词 | 贪婪量词 |
---|---|
{m,n}? | {m,n} |
{m,}? | {m,} |
?? | ? |
+? | + |
*? | * |
TIP : 对惰性匹配的记忆方式是:量词后面加个问号,问一问你知足了吗,你很贪婪吗?
多选分支 : (p1|p2|p3),其中 p1、p2 和 p3 是子模式,用 |(管道符)分隔,表示其中任何之一
案列分析
// 匹配时间
var regex = /^(0?[0-9]|1[0-9]|[2][0-3]):(0?[0-9]|[1-5][0-9])$/;
console.log( regex.test("23:59") );
console.log( regex.test("02:07") );
console.log( regex.test("7:9") );
// => true
// => true
// => true
// 匹配id
var regex = /id=".*?"/ // 一定要加惰性匹配,不然就是贪婪匹配,结果会错误的
var string = '<div id="container" class="main"></div>'; console.log(string.match(regex)[0]);
// => id="container"
或者如下
// 匹配id
var regex = /id="[^"]*"/ // 先匹配id=" 然后匹配任何不是"的任意个字符 最后再匹配"
var string = '<div id="container" class="main"></div>'; console.log(string.match(regex)[0]);
// => id="container"
二、位置匹配攻略
- 关于位置的6个锚
锚 | 含义 |
---|---|
^ | (脱字符)匹配开头,在多行匹配中匹配行开头 |
$ | (美元符号)匹配结尾,在多行匹配中匹配行结尾 |
\b | 单词边界,具体就是 \w 与 \W 之间的位置,也包括 \w 与 ^ 之间的位置,和 \w 与 $ 之间的位置 |
\B | \b 的反面的意思,非单词边界 |
(?=p) | 正向断言(预查) ;检查某个字符后面的字符是否满足某个规则,该规则不成为匹配结果,并且不成为捕获组 |
(?!p) | 负向断言(预查); 检查某个字符后面的字符是否不满足某个规则,该规则不成为匹配结果,并且不成为捕获组 |
- 案列分析
// 数字的千位分隔符表示法
var regex = /(?!^)(?=(\d{3})+$)/g;
var result = "12345678".replace(regex, ',') console.log(result);
// => "12,345,678"
result = "123456789".replace(regex, ','); console.log(result);
// => "123,456,789"
三、正则表达式括号的作用
- 分组:
var regex = /(ab)+/g;
其中括号是提供分组功能,使量词 + 作用于 "ab" 这个整体 - 分支结构: 而在多选分支结构 (p1|p2) 中,此处括号的作用也是不言而喻的,提供了分支表达式的所有可能
- 提取数据:可以从数组中获取,也可以从构造函数饿全局属性
9来获取
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
console.log( string.match(regex) );
// => ["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12"]
match
返回的一个数组,第一个元素是整体匹配结果,然后是各个分组(括号里)匹配的 NOTE 内容,然后是匹配下标,最后是输入的文本
另外也可以使用正则实例对象的 exec 方法
- 替换
// 比如,想把 yyyy-mm-dd 格式,替换成 mm/dd/yyyy
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
var result = string.replace(regex, "$2/$3/$1"); console.log(result);
// => "06/12/2017"
- 反向引用 :
\捕获组编号
// 写一个正则支持匹配如下三种格式
// 2016-06-12
// 2016/06/12
// 2016.06.12
var regex = /\d{4}(-|\/|\.)\d{2}\1\d{2}/; var string1 = "2017-06-12";
var string2 = "2017/06/12";
var string3 = "2017.06.12";
var string4 = "2016-06/12";
console.log( regex.test(string1) ); // true
console.log( regex.test(string2) ); // true
console.log( regex.test(string3) ); // true
console.log( regex.test(string4) ); // false
- 分组后面有量词: 分组后面有量词的话,分组最终捕获到的数据是最后一次的匹配
// 分组 (\d) 捕获的数据是 "5"
var regex = /(\d)+/;
var string = "12345";
console.log( string.match(regex) );
// => ["12345", "5", index: 0, input: "12345"]
var regex = /(\d)+ \1/;
console.log( regex.test("12345 1") ); // => false
console.log( regex.test("12345 5") ); // => true
- 非捕获符号:
(?:)
只想要括号最原始的功能, 不会成为捕获组 - 案例分析
// 字符串 trim 方法模拟
function trim(str) {
return str.replace(/^\s+|\s+$/g, '');
}
console.log( trim(" foobar ") ); // => "foobar"
// 字符串 trim 方法模拟
function trim (str) {
return str.replace(/^\s*(.*?)\s*$/g, "$1");
}
console.log( trim(" foobar ") );
// => "foobar"
// 这里使用了惰性匹配 *?,不然也会匹配最后一个空格之前的所有空格的
// 非捕获组
// 将每个单词的首字母转换为大写
function titleize (str) {
return str.toLowerCase().replace(/(?:^|\s)\w/g, function (c) {
return c.toUpperCase(); });
}
console.log( titleize('my name is epeli') );
// => "My Name Is Epeli"
四、回溯法
简单总结就是,正因为有多种可能,所以要一个一个试。直到,要么到某一步时,整体匹配成功了;要么最后都试完后,发现整体匹配不成功。
贪婪量词“试”的策略是:买衣服砍价。价钱太高了,便宜点,不行,再便宜点。
惰性量词“试”的策略是:卖东西加价。给少了,再多给点行不,还有点少啊,再给点。
分支结构“试”的策略是:货比三家。这家不行,换一家吧,还不行,再换。
五、正则表达式的拆分
结构 | 说明 |
---|---|
字面量 | 匹配一个具体字符,包括不用转义的和需要转义的。比如 a 匹配字符 "a", 又比如 \n 匹配换行符,又比如 . 匹配小数点。 |
字符组 | 匹配一个字符,可以是多种可能之一,比如 [0-9],表示匹配一个数字。也有 \d 的简写形式。 另外还有反义字符组,表示可以是除了特定字符之外任何一个字符,比如 [^0-9], 表示一个非数字字符,也有 \D 的简写形式。 |
量词 | 表示一个字符连续出现,比如 a{1,3} 表示 "a" 字符连续出现1到 3 次。 另外还有常见的简写形式,比如 a+ 表示 "a" 字符连续出现至少一次。 |
锚 | 匹配一个位置,而不是字符。比如 ^ 匹配字符串的开头,又比如 \b 匹配单词边界, 又比如 (?=\d) 表示数字前面的位置。 |
分组 | 用括号表示一个整体,比如 (ab)+,表示 "ab" 两个字符连续出现多次, 也可以使用非捕获分组 (?:ab)+。 |
分支 | 多个子表达式多选一,比如 abc|bcd,表达式匹配 "abc" 或者 "bcd" 字符子串。 反向引用,比如 \2,表示引用第 2 个分组。 |
- 操作符
操作符描述 | 操作符 | 优先级 |
---|---|---|
转义符 | \ | 1 |
括号和方括号 | (...)、(?:...)、(?=...)、(?!...)、[...] | 2 |
量词限定符 | {m}、{m,n}、{m,}、?、*、+ | 3 |
位置和序列 | ^、$、\元字符、一般字符 | 4 |
管道符(竖杠) | | | 5 |
- 所有结构里,用到的元字符总结如下:
^、$、.、*、+、?、|、\、/、(、)、[、]、{、}、=、!、:、- ,
六、replace 是很强大的
总体来说 replace 有两种使用形式,这是因为它的第二个参数,可以是字符串,也可以是函数。 当第二个参数是字符串时,如下的字符有特殊的含义:
属性 | 描述 |
---|---|
|
匹配第 1-99 个 分组里捕获的文本 |
$& | 匹配到的子串文本 |
$` | 匹配到的子串的左边文本 |
$' | 匹配到的子串的右边文本 |
$$ | 美元符号 |
- 案例分析
// 把 "2,3,5",变成 "5=2+3"
var result = "2,3,5".replace(/(\d+),(\d+),(\d+)/, "$3=$1+$2");
console.log(result);
// => "5=2+3"
// 把 "2,3,5",变成 "222,333,555"
var result = "2,3,5".replace(/(\d+)/g, "$&$&$&");
console.log(result);
// => "222,333,555"
// 把 "2+3=5",变成 "2+3=2+3=5=5"
var result = "2+3=5".replace(/=/, "$&$`$&$'$&");
console.log(result);
// => "2+3=2+3=5=5"