共计 6354 个字符,预计需要花费 16 分钟才能阅读完成。
API 使用
String#search
String#split
String#match
String#replace
RegExp#test
RegExp#exec
看完你就会正则表达式了四种操作
验证
切分
提取
替换
test search match exec
第一章 正则表达式字符匹配攻略
正则表达式是匹配模式,要么匹配字符,要么匹配位置
{m,n} 至少 m,至多 n
[a-z] 匹配 a - z 中的任一个字符 – 范围表示法
如果要匹配 a – z 则[-az] [az-] [a\-z]
[abc] 要么 a 要么 b 要么 c
\d === [0-9]
\D === [^0-9]
\w === [0-9a-zA-Z_]
\W === [^0-9a-zA-Z_]
\s === [\t\v\n\r\f]
. 通配符,表示几乎任意字符
{m} 表示出现 m 次
要匹配任意字符 可以使用[dD] [wW] [sS] [^] 中的任意一个
贪婪匹配惰性匹配?
var regex = /\d{2,5}?/g
var string = “123 3456 1290 13498”
console.log(string.match(regex))
// 当两个足够的时候就不在往下匹配了
[“12”, “34”, “56”, “12”, “90”, “13”, “49”]
贪婪量词
{m,n}
{m,}
?
+
*
惰性量词 在贪婪量词后面加上?就行
多选分支 | 也是惰性的
var regex = /good|goodbye/g
var string = “goodbye”
console.log(string.mathc(regex)) // good
匹配到一个了,后面的就不再尝试了
匹配 16 进制颜色值
var regex = /#([0-9A-Fa-f])|(0-9A-Fa-f){3}/g
匹配时间
var regex = /^([01][0-9]|[2][0-3]) : [0-5][0-9]$/
console.log(regex.test(“23:59”)) //true
如果要求匹配 7:9 前面的 0 可以省略
var regex = /^(0?[0-9]|1[0-9]|[2][0-3]):(0?[0-9]|[1-5][0-9])$/;
匹配年月日
year [0-9]{4}
month (0[1-9]|1[0-2]) 12 个月
day 最大 31 (0[1-9]|[12][0-9]|3[01]) 01 02…09 10,11….20,21….30,31
var regex = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;
console.log(regex.test(“2017-06-10″) ); //true
匹配 id
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)
多行匹配模式 (即有修饰符 m) 时,二者是行的概念,这一点需要我们注意:
\n 换行
var result = “I\nlove\njavascript”.replace(/^|$/gm, ‘#’);
console.log(result);
/*
#I#
#love#
#javascript#
*/
b 是单词变界 具体是 w 与 W 之间的位置 w 与 ^ w 与 $ 之间的位置
var result = “[JS] Lesson_01.mp4”.replace(/\b/g, ‘#’);
console.log(result);
// => “[#JS#] #Lesson_01#.#mp4#”
B : w 与 w、W 与 W、^ 与 W,W 与 $ 之间的位置
var result = “[JS] Lesson_01.mp4”.replace(/\B/g, ‘#’);
console.log(result);
// => “#[J#S]# L#e#s#s#o#n#_#0#1.m#p#4”
(?=p) p 是一个子模式,即 p 前面的位置,或者说,该位置后面的字符要匹配 p
var result = “hello”.replace(/(?=l)/g, ‘#’);
console.log(result);
// => “he#l#lo”
(?!p) 而 (?!p) 就是 (?=p) 的反面意思,比如:
var result = “hello”.replace(/(?!l)/g, ‘#’);
console.log(result);
// => “#h#ell#o#”
把位置理解空字符
千位分隔符
var regex = /(?!^)(?=(\d{3})+$)/g
var result = ‘1234567’.replace(regex, ‘,’)
如果要把 “12345678 123456789” 替换成 “12,345,678 123,456,789″。此时我们需要修改正则,把里面的开头 ^ 和结尾
$,修改成 \b: var string = “12345678 123456789”,
regex = /(?!\b)(?=(\d{3})+\b)/g;
var result = string.replace(regex, ‘,’)
console.log(result);
// => “12,345,678 123,456,789”
(?!b) == B
货币案例
function format (num) {
return num.toFixed(2).replace(/\B(?=(\d{3})+\b)/g, “,”).replace(/^/, “$ “);
};
console.log(format(1888) );
// => “$ 1,888.00”
第三章 分组和分支结构
()|
分组引用
分组可以捕获数据 - 分组引用替换
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”
// 其中 replace 中的,第二个参数里用 $1、$2、$3 指代相应的分组。等价于如下的形式:
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = “2017-06-12”;
var result = string.replace(regex, function () {
return RegExp.$2 + “/” + RegExp.$3 + “/” + RegExp.$1;
});
console.log(result);
// => “06/12/2017”
也等价于:
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = “2017-06-12”;
var result = string.replace(regex, function (match, year, month, day) {
return month + “/” + day + “/” + year;
});
console.log(result);
// => “06/12/2017”
- 反向引用 — 引用之前出现的分组
regex.test(string) regex.exec(string) string.match(regex) RegExp.$1
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
\1 表示前面引用的那个分组 不管前面这个分组比如 (-|\/|\.) 匹配到什么 比如 - \1 都匹配那个同样的具体某个字符
\2 \3 表示指代第二个和第三个分组
tips 10 表示第 10 个分组那么要匹配 1 和 0 的话,(?:1)0 1(?:0)
反向引用 是引用前面的分组,但是如果正则里面引用了不存在的分组时,只是匹配了反向引用的字符本身
分组后面有量词会怎么样?
分组后面有量词的话,分组最终捕获到的数据是最后一次的匹配
var regex = /(\d)+/;
var string = “12345”;
console.log(string.match(regex) );
// => [“12345”, “5”, index: 0, input: “12345”]
从上面看出,分组 (\d) 捕获的数据是 “5”。
同理对于反向引用,也是这样的。测试如下:
var regex = /(\d)+ \1/;
console.log(regex.test(“12345 1”) );
// => false
console.log(regex.test(“12345 5”) );
// => true
非捕获括号 (?:p) (?:p1|p2|p3)
如果只想要括号最原始的功能,但不会引用它,即,既不在 API 里引用,也不在正则里反向引用,此时可以使用非捕获括号(?:p) 和 (?:p1|p2|p3)
var regex = /(?:ab)+/g;
var string = “ababa abbb ababab”;
console.log(string.match(regex) );
// => [“abab”, “ab”, “ababab”]
同理,第二例子可以修改为:
var regex = /^I love (?:JavaScript|Regular Expression)$/;
console.log(regex.test(“I love JavaScript”) );
console.log(regex.test(“I love Regular Expression”) );
// => true
// => true
捕获与非捕获括号区别
var str = “a1***ab1cd2***c2″;
var reg1 = /((ab)+\d+)((cd)+\d+)/i;
var reg2 = /((?:ab)+\d+)((?:cd)+\d+)/i;
alert(str.match(reg1));//ab1cd2,ab1,ab,cd2,cd
alert(str.match(reg2));//ab1cd2,ab1,cd2
// 可以看出捕获分组和非捕获分组的区别了吧:非捕获分组,只是用来匹配,并不会提取分组内容。也就是说,如果我们只想用圆括号将一些字符用数量词修饰,并不需要这个分组的内容,这就是非捕获分组
replace 用法
语法:stringObj.replace(regexp/substr,replacement)
NO.5 第一个参数是正则且有子表达式,第二个参数函数且带有多个参数 var str5 = ‘ 这是一段原始文本,需要替换的内容 ”3c 这要替换 4d”!’;
var newStr = str5.replace(/([0-9])([a-z])/g,function (arg1,arg2,arg3,arg4,arg5){
console.log(arg1);
console.log(arg2);
console.log(arg3);
console.log(arg4);
console.log(arg5);
} );
// 输出:
3c
3
c
17
这是一段原始文本,需要替换的内容 ”3c 这要替换 4d”!
4d
4
d
23
这是一段原始文本,需要替换的内容 ”3c 这要替换 4d”!
上面的例子第一个参数 arg1 表示匹配的整体,arg2 表示第一个子表达式,arg3 表示第二个子表达式,接下来的参数 arg4 是一个整数,声明了表示子匹配在 stringObject 中出现的位置。最后一个参数是 stringObject 本身。
NO.3 第一个参数是正则,第二个参数是带 $ 符的字符串
var str3 = ‘ 这是一段原始文本,”3c 这要替换 4d”!’;
var newStr = str3.replace(/([0-9])([a-z])/g,”$1″ );
console.log(newStr); // 输出:这是一段原始文本,”3 这要替换 4 ″!’;
var newStr = str3.replace(/([0-9])([a-z])/g,”$2″ ); // 这是一段原始文本,”c 这要替换 d”!’;
cankao 案例
驼峰转换
如果不加? 会多匹配一个空格 加问号后,因为? 是懒得匹配,所以不会匹配最后一个空格
function camelize (str) {
return str.replace(/[-_\s]+(.)?/g, function (match, c) {console.log(c,’c’)
return c ? c.toUpperCase() : ”;
});
}
console.log(camelize(‘-moz-transform ‘) );
<!–VM2177:2 m c–>
<!–VM2177:2 t c–>
<!–VM2177:2 undefined “c”–>
<!–VM2177:6 MozTransform–>
其中分组 (.) 表示首字母。单词的界定是,前面的字符可以是多个连字符、下划线以及空白符。正则后面的 ? 的目的,是为了应对 str 尾部的字符可能不是单词字符,比如 str 是 ‘-moz-transform ‘
第五章 正则表达式的拆分匹配字符串比如匹配 “[abc]” 和 “{3,5}”
var string = “[abc]”
var regex = /\[abc]/g
console.log(string.macth(regex)[0])
在第一个方括号转义即可 因为后面的方括号构不成字符组
var string = “JavaScript”;
console.log(string.match(/.{4}(.+)/)[1] );
// => Script
// 前面的点出现四次 后面匹配的点出现一次以上
密码匹配问题: 要求密码长度 6-12 位,由数字、小写字符和大写字母组成,但必须至少包括 2 种字符
var regex1 = /^[0-9A-Za-z]{6,12}$/;
var regex2 = /^[0-9]{6,12}$/;
var regex3 = /^[A-Z]{6,12}$/;
var regex4 = /^[a-z]{6,12}$/;
function checkPassword (string) {
if (!regex1.test(string)) return false;
if (regex2.test(string)) return false;
if (regex3.test(string)) return false;
if (regex4.test(string)) return false;
return true;
}
匹配浮点数 符号 [+-] 整数部分 d+ 小数部分 .d+
匹配 1.23 +1.23 -1.23 10 +10 -10 .2 +.2 -.2
/^[+-]?(\d\.\d+|\d+|\.\d+)$/
第七章节 正则表达式编程
正则表达式的四种操纵
验证 切分 提取 替换