目录
1. 为什么需要正则表达式
- 正则表达式可以用来:
- 操作HTML节点中的字符串
- 使用CSS选择器表达式定位部分选择器
- 判断一个元素是否具有指定的类名(class)
- 输入校验
- 其他任务
2. 正则表达式进阶
2.1 正则表达式说明
创建正则表达式
// 字面量创建
const pattern = /test/ig;
// 实例化正则对象
const pattern = new RegExp("test", "ig");
当正则表达式再开发环境是明确的,推荐有限使用字面量语法;当需要再运行时动态创建字符串来构建正则表达式时,则使用构造函数的方式
5个修饰符
修饰符 | 作用 |
---|---|
i | ignore,忽略大小写 |
g | global,查找所有匹配项 |
m | multiple,允许多行匹配,对获取textarea元素的值很有用 |
y | 开启粘连匹配。正则表达式执行粘连匹配时试图从最后一个匹配的位置开始 |
u | 允许使用Unicode点转义字符(u{...}) |
2.2 术语和操作符
名称 | 示例 | 示例含义 |
---|---|---|
精确匹配 | /test/ | 4个字母必须完全出现在所匹配的字符串中 |
匹配字符集 | [abc] | 匹配a、b、c中的任意一个字符 |
[^abc] | 匹配除了a、b、c的任意字符 | |
[a-z] | 匹配从a到z所有小写字母 | |
转义 | [] | 让程序将[]识别为字符,而不是操作符 |
起止符号 | /^test/ | 匹配以test开头的字符串 |
/test$/ | 匹配以test结束的字符串 | |
重复出现 | /t?est/ | 同时匹配est和test 在字符后面加?表示这个字符可以出现0次或1次 |
/t+est/ | 匹配test、ttest、tttest等 在字符后面加+表示这个字符可以出现1次或多次 |
|
/t*est/ | 匹配est、test、ttest、tttest等 在字符后面加*表示这个字符可以出现0次或1次或多次 |
|
/a{4}/ | 匹配4个连续的字符a 用大括号指定重复次数 |
|
/a{2,4} | 匹配2~4个连续的字符a | |
/a{2,} | 匹配2个或更多个连续的字符a | |
分组 | /(ab)+ | 匹配一个或多个连续的ab 括号扩起表示一个整体,共享后面的操作符 |
或(or) | /a|b/ | 匹配字符a或者字符b |
反向引用 | /^([dtn])a1/ | 匹配以字符d、t或n开头,其后连接字符a, 再后连接一个第一个分组(也就是括号中)中捕获的内容 1匹配的具体字母要在运行时才能确定 引用正则中定义的捕获,第一个分组的捕获为1,第二个为2 反向引用在匹配XML类型的标记元素时很有用,如/<(w+)>(.+)</1>/ |
表示重复出现的运算符默认是贪婪模式,可以匹配所有可能的字符。在运算符后添加?使运算符为非贪婪模式,只进行最小限度的匹配。如对于字符串aaa,/a+/会匹配aaa,/a+?/则会匹配a、a、a。
预定义元字符 | 匹配的字符集 |
---|---|
水平制表符 | |
空格 | |
v | 垂直制表符 |
f | 换页符 |
回车符 | |
换行符 | |
cA:cZ | 控制字符 |
u0000:uFFFF | 十六进制Unicode码 |
x00:xFF | 十六进制ASCII码 |
. | 匹配除换行字符( 、 、u2028和u2029)之外的任意字符 |
d | 匹配任意十进制数字,等价于[0-9] |
D | 匹配除了十进制数字的任意字符,等价于[^0-9] |
w | 匹配任何字母、数字和下划线,等价于[A-Za-z0-9_] |
W | 匹配除了字母、数字和下划线的任意字符,等价于[^A-Za-z0-9_] |
s | 匹配任意空白字符(包括空格、制表符、换页符等) |
S | 匹配除了空白字符的任意字符 |
匹配单词边界 | |
B | 匹配非单词边界(单词内部) |
3. 编译正则表达式
3.1 创建正则表达式的两种方法
// 字面量创建
const re1 = /test/i;
// 实例化正则对象
const re2 = new RegExp("test", "i");
console.log(re1.toString() === "/test/i");
// true
// 用test方法检验字符串
console.log(re1.test("Test"));
// true
console.log(re2.test("tesi"));
// false
// 每个正则对象都是独一无二的
console.log(re1.toString() === re2.toString());
// true
console.log(re1 === re2);
// false
3.2 在运行时编译一个供稍后使用的正则表达式
<div id="wango" class="human student son brother"></div>
<span></span>
<div id="lily" class="human student daughter sister"></div>
<div id="jack" class="human teacher son father"></div>
<script>
function findClassInElements(className, type) {
// getElementsByTagName方法返回的不是数组,是 HTMLCollection(HTML集合)
// 在WebKit内核的浏览器中返回一个 NodeList,所以不能使用数组方法
const elems = document.getElementsByTagName(type || "*");
// 类名的规则:直接开头或者以空格开头,直接结束或者以空格结束
// 当创建正则表达式字面量时,只需使用一个反斜线(s)
// 但这里是在字符串中写反斜线,就必须使用双反斜线(\s)进行转义
const reg = new RegExp("(^|\s)" + className + "(\s|$)");
const results = [];
// 在for循环中先将长度储存,避免每次循环都去访问对象再返回长度,
// 以优化性能(长度不能是动态的)
for (let i = 0, len = elems.length; i < len; i++) {
if (reg.test(elems[i].className)) {
results.push(elems[i]);
}
}
return results;
}
findClassInElements("student", "div").forEach(elem => {
console.log(elem.id);
});
// wango
// lily
findClassInElements("son").forEach(elem => {
console.log(elem.id);
});
// wango
// jack
</script>
4. 捕获匹配的片段
4.1 执行简单捕获
<div id="square" style="transform: translateY(15px);background-color: black;"></div>
<script>
function getTransformY(elem) {
const transformValue = elem.style.transform;
if (transformValue) {
// 用括号分组,使用match方法(由字符串调用)捕获匹配的值,返回数组
const match = transformValue.match(/^translateY(([^)]+))$/);
// const match = transformValue.match(/^translateY((.+))$/);
// match方法匹配结果通过第一个索引返回,然后每次捕获结果索引递增
return match ? match[1] : "";
}
return "";
}
const square = document.getElementById("square");
console.log(getTransformY(square));
// 15px
</script>
4.2 使用全局表达式进行匹配
const html = '<div class="test"><b>hello</b><i>world!</i></div>';
// 使用局部正则表达式可以返回数组,
// 数组包含第一个匹配的全部内容以及操作中的全部捕获结果
const results = html.match(/<(/?)(w+)([^>]*?)>/);
console.log(results[0]); // <div class="test">
console.log(results[1]); //
console.log(results[2]); // div
console.log(results[3]); // class="test"
// 使用全局正则表达式可以返回数组
// 数组包含全部匹配结果,但不返回捕获内容
const all = html.match(/<(/?)(w+)([^>]*?)>/g);
console.log(all[0]); // <div class="test">
console.log(all[1]); // <b>
console.log(all[2]); // </b>
console.log(all[3]); // <i>
console.log(all[4]); // </i>
console.log(all[5]); // </div>
let match;
const rslts = [];
const reg = /<(/?)(w+)([^>]*?)>/g;
// 使用exec方法(正则调用)进行捕获和全局搜索
// 每次调用都可以返回下一个匹配的结果,
// 结果包含第一个匹配的全部内容以及操作中的全部捕获
while ((match = reg.exec(html)) !== null) {
rslts.push(match);
}
console.log(rslts[0][0]); // <div class="test">
console.log(rslts[0][1]); //
console.log(rslts[0][2]); // div
console.log(rslts[0][3]); // class="test"
4.3 捕获的引用
<b class="hello">Hello</b><i>world!</i>
<script>
const html = '<b class="hello">Hello</b><i>world!</i>';
// 使用1指向正则表达式中的第1个捕获
const reg = /<(w+)([^>]*)>(.*?)</1>/g // 仅作示例,并不完美
let match = reg.exec(html);
console.log(match[0]); // <b class="hello">Hello</b>
console.log(match[1]); // b
console.log(match[2]); // class="hello"
console.log(match[3]); // Hello
</script>
4.4 未捕获的分组
// 用作分组的括号也会被识别为捕获内容
const reg1 = /((hello-)+)world/;
let match = "hello-hello-world".match(reg1);
console.log(match[0]); // hello-hello-world
console.log(match[1]); // hello-hello-
console.log(match[2]); // hello-
// 在起始圆括号之后使用?:创建被动子表达式,使不被捕获
const reg2 = /((?:hello-)+)world/;
match = "hello-hello-world".match(reg2);
console.log(match[0]); // hello-hello-world
console.log(match[1]); // hello-hello-
console.log(match[2]); // undefined
5. 利用函数进行替换(replace方法)
// 基本用法
console.log("ABCDEfg".replace(/[A-Z]/g, "X"));
// XXXXXfg
// replace方法第二个参数可以是函数,
// 这时,对每一个匹配到的值都会调用一次这个函数,
// 从函数返回的值作为匹配字符串的替换值
// 将下划线连接的字符串转换为小驼峰字符串
// 第二个参数函数接收多个参数,第一个是匹配的字符串,之后的参数是捕获结果
console.log("this_is_an_array".replace(/_(w)/g, (all, letter) => letter.toUpperCase()));
// thisIsAnArray
// 一种查询字符串压缩技术
function compress(str) {
const keys = {}; // 存储键值对
// 提取键值对信息
str.replace(/([^=&]+)=([^&]*)/g, (all, key, value) => {
keys[key] = (keys[key] ? keys[key] + "," : "") + value;
return "";
// 只是利用replace的查找和调用函数功能,
// 不在意执行结果,所以可以返回任意值
});
// 收集key信息
const result = [];
for (let key in keys) {
result.push(key + "=" + keys[key]);
}
return result.join("&");
}
const str = "foo=1&foo=2&blah=a&blah=b&foo=3";
console.log(compress(str));
// foo=1,2,3&blah=a,b
6. 使用正则表达式解决常见的问题
6.1 匹配换行
const html = "<b>Hello</b>
<i>world!</i>";
console.log(/.*/.exec(html)[0]); // 不匹配换行符
// <b>Hello</b>
console.log(/[Ss]*/.exec(html)[0]); // 匹配全部字符(最优解)
// <b>Hello</b>
// <i>world!</i>
console.log(/(?:.|s)*/.exec(html)[0]); // 匹配换行符
// <b>Hello</b>
// <i>world!</i>
6.2 匹配Unicode字符
const text = "u6211u7231u4f60uff0cu4e2du56fduff01";
const mathchAll = /[wu0080-uFFFF_-]+/; // 匹配所有字符,包括Unicode
console.log(text.match(mathchAll)[0]);
// 我爱你,中国!
6.3 匹配转义字符
// 允许任意字符序列组成的词,包括一个反斜线紧跟
// 任意字符(包括反斜线本身),或兼而有之
const pattern = /^((w+)|(\.))+$/;
// 测试字符串,除了最后一个,其他均通过
const tests = [
"formUpdate",
"form\.update\.whatever",
"form\:update",
"\f\o\r\m\u\p\d\a\t\e",
"form:update"
];
let flag = true;
for (let i = 0, len = tests.length; i < len; i++) {
if (!pattern.test(tests[i])) {
flag = false;
console.log(tests[i], "is invalid!");
};
}
console.log(flag);
// form:update is invalid!
// false