正则表达式
正则表达式(简写为 regexp) 用正则制定的规则,对字符串进行处理。如(查找 替换 匹配某个字符串是否符合规则等)。在 JS 中主要用正则表达式来处理字符串。
编程中的日常应用:
表单验证(验证用户填入的信息 邮箱、用户名、密码格式是否合法)
日期格式化(2019-01-02 => 2019/01/02)
查找匹配的关键词('hello2018 hello2019' => [2018, 2019])
对文本内容进行匹配替换
掌握了正则表达式,可能为你带来超乎想象的文本处理能力。有时只是一个简单的命令,就可以帮你处理所有文本数据。
其实像我们的文本编辑器、IDE 都支持使用正则查找关键词。
正则表达式语法
字面量方式
// 在两个斜杠之间的字符内容称为元字符 /元字符/
const reg = /\d/igm // \d是元字符代表[0-9]任意一位数 igm是修饰符
实例化方式
// 实例化方式边界不需要加斜杠 对于特殊元字符需要加上转义斜杠
const reg = new RegExp('\\d', 'igm')
// 可以拼接变量
const cn = 'china'
const reg1 = new RegExp(`${cn}\\d+`)
console.log(reg1) // /china\d+/
正则表达式中的元字符和修饰符
每一个正则表达式都是由元字符和修饰符组成的
// \d是特殊元字符 代表[0-9]中任意一个数字 igm是修饰符
const reg1 = /\d/igm
// 实例化方式边界不需要加斜杠 对于特殊元字符前面需要加上转义斜杠
const reg = new RegExp('\\d', 'igm')
正则匹配
在 RegExp 原型有一个 test 方法,用来验证字符内容是否符合正则。定义的每个正则都是 RegExp 类的实例。
语法:
reg.test('字符内容')
const reg = /\d/
reg.test('1') // true
修饰符
i ignoreCase 忽略大小写
g global 全局匹配
m multiline 多行匹配
特殊元字符
在正则中具有特殊意义的字符
转义字符
d 匹配 0-9 的数字 包含 0 和 9 相当于[0-9]
D 除了 0-9 的任意字符
w 匹配数字、字母、下划线 相当于[0-9a-zA-Z_]
W 匹配除了数字、字母、下划线以外的其他字符,相当于1
s 匹配空白符(空格s 换行rn 制表符 tab)
S 匹配除了空白符(空格 换行 制表符)以外的字符
b 匹配单词边界 'hello world'
n 匹配换行符
^ 匹配字符串的开始位置(以某个元字符作为开头)
$ 匹配字符串的结束位置(以某个元字符作为结尾)
. 匹配除了n 以外的任意字符(在这里默认不是小数点的意思 可以转义为小数点 .)
console.log(/./.test('\n')) // false
console.log(/./.test('1')) // true
console.log(/./.test('w')) // true
console.log(/./.test('_')) // true
console.log(/./.test('.')) // true
// \. 转义为普通小数点
console.log(/\./.test('.')) // true
console.log(/\./.test('1')) // false
console.log(/\./.test('w')) // false
其他元字符
x|y x 或者 y
[xyz] 匹配 xyz 中的任意一个字符
2 匹配除了 xyz 以外的任意一个字符
[a-z] 匹配 a 到 z 中的任意一个字符 [0-9] === d
3 匹配除了 a 到 z 以外的任意一个字符 4 === D
() 分组捕获 括号中的内容单独捕获一次
(? 只匹配不捕获
(?=) 正向预查
(?!) 负向预查
量词元字符
? 出现 0 次或 1 一次(最多出现一次 可有可无)
- 代表出现 0 到多次,相当于{0,}
- 代表出现 1 到多次(至少出现一次),相当于{1,}
{n} 连续出现 n 次
{n,} 连续出现 n 到多次
{n,m} 连续出现 n 都 m 次(最少 n 次 最多 m 次)
普通元字符
/abc/
// 正则:由元字符和修饰符组成 处理字符串 匹配验证 捕获 的规则
// RegExp
// 元字符:/\dasc/ 两个斜杠之间的就是元字符 特殊元字符 量词元字符 普通元字符
// 修饰符 /sdfds/igm
// 1.特殊元字符
// \ 转义符 转换为本来意义
// . 任意字符 除了\n(换行符) /./ /\./
// \n 匹配换行符
// \d 0-9之间的一个数字 /\d/ [0-9]
// \D 除了\d 除的了0-9数字以外 /\D/ [^0-9]
// \b 匹配边界 '123 a12' /\b\d/ /\d\b/
// \B 非边界 '123 a12' /\B\d/匹配的是不以数字作为开头的边界 /\d\B/
// \w 数字0-9字母a-z A-Z下划线_ 等价于 [0-9a-zA-Z_]
// \W 除了\w 除了数字0-9字母a-z A-Z下划线_以外的字符
// \s 空格符
// \S 非空格
// [xyz] x或者y或者z 中的一个 '1x2y'
// [^xyz] 除了x或者y或者z中的 其他字符 'x123'
// [a-z] 匹配a-z中任意一个小写字母 'a'
// [^a-z] 除了a-z中任意一个字符
// x|y|z x或者y或者z
// () 分组 改变优先级 划分小正则 分组引用
// ^ 以什么开头 /^\d/ '12' '1a'
// $ 以什么结尾 /\d$/ 'a2'
// ?: 只匹配不捕获
// ?= 正向预查
// ?! 负向预查
// 2.量词元字符
// ? 出现0到1次(可有可无) /a?/ '1a23'
// * 出现0到多次 /a*/ '12aaaaaaaa312'
// + 出现1到多次 至少出现1次 /a+/ '123aaaaa12'
// {n} 限定出现n次 匹配只能出现n次 /a{2}/ '1aa1'
// {n,} 限定出现n到多次 至少出现n次 /a{2,}/ '1aaaaaa1'
// {n,m} 限定出现n到m次 /a{2, 5}/ '113aaa5' 出现2-5次 2 3 4 5
// 3.普通元字符
/abcd/
// 修饰符
// i ignoreCase 忽略大小写 /[a-z]/i 'a' 'A'
// g global 全局 /[a-z]/g 's'
// m multiline 逐行匹配 /^\d/ '123123'
正则中括号
/**
* [18] 不识别多位数 1 或者 8
* [12-68] 1或者 [2-6] 或者8
* [] 中括号里 部分元字符代表本意 [.] [?] [+] [*]
* [\d] [\s] [\w] 代表是还是原本特殊的意义
*/
[]中不识别多位数
// [18] 不识别多位数 1 或者 8
let reg = /^[18]$/
console.log(reg.test('1')) // true
console.log(reg.test('8')) // true
console.log(reg.test('a')) // false
[12-68] 1 或者 [2-6] 或者 8
let reg = /^[12-68]$/
console.log(reg.test('1')) // true
console.log(reg.test('2')) // true
console.log(reg.test('6')) // true
console.log(reg.test('8')) // true
console.log(reg.test('9')) // false
[] 中括号里部分元字符 代表普通元字符
// [\d] [\s] [\w] 代表是还是原本的特殊意义
// [\d] [0-9]
let reg = /[\d]/
reg.test('1') // true
// 代表普通元字符 [.] [?] [+] [*]
let reg = /[\w]/
console.log(reg.test('1'))
let reg = /[*]/
console.log(reg.test('*')) // true
let reg = /[+]/
console.log(reg.test('+')) // true
let reg = /[\d]/
console.log(reg.test('2')) // true
正则匹配和捕获
匹配:验证字符内容是否符合正则规则 返回 true 或 false
捕获: 将匹配(符合正则规则)的内容 作为返回值返回给我们
RegExp 原型上的两个方法
// test 检测字符串内容是否符合(匹配)正则规则。返回值: boolean值
RegExp.prototype.test('字符内容')
// exec 将匹配的内容,捕获出来返回给我们。返回值:null或数组形式
RegExp.prototype.exec('字符内容')
test
reg.test('字符内容')
/\d/.test('1') // true
exec
reg.exec('字符内容')
/**
["1", index: 0, input: "123", groups: undefined]
1.数组中从第一项开始时 是正则捕获到的内容,如果有分组继续往后排
2.index 正则捕获内容的起始索引位置
3.input 原始字符串
4.groups 捕获命名分组内容
*/
// ["1", index: 0, input: "123", groups: undefined]
/\d/.exec('123')
关于[]细节问题
// /[81]/ 不能识别多位数 看作是8或者1
// [a-z] a到z 26个小写字母
// [13-56] 1 或者 3到5 或者6
// []中括号里部分元字符代表的是本身意义 [+] '+' [?] '?' [.] '.'
// [\d] 依然代表的是 0-9中的一个数字
正则的一些简单应用
验证手机号
// 2. 手机号 188 176 138 11位数 开头只能是1
var reg = /^1(88|76|38)\d{8}$/; // ^$只能是11位数 不能有其他字符
reg.test('17645678999')
reg.test('18845678999')
reg.test('13845678999')
验证有效数字
let reg = /^[+-]?(\d|[1-9]\d)(\.\d+)?$/
console.log(reg.test('1'))
console.log(reg.test('-21'))
console.log(reg.test('1.1'))
console.log(reg.test('0'))
匹配某个范围内的数字
// 28-66
let reg = /^(2[89]|[2-5]\d|6[0-6])$/
console.log(reg.test('17'))
console.log(reg.test('18'))
console.log(reg.test('38'))
console.log(reg.test('65'))
console.log(reg.test('66'))
中文姓名
let reg = /^[\u4E00-\u9FA5]{2,10}$/
console.log(reg.test('adfs'))
console.log(reg.test('物理'))
验证邮箱
// 雅虎邮箱 XXXXXX@yahoo.com.cn
// 雅虎邮箱 XXXXXX@yahoo.cn
// Google XXXXX@gmail.com
// QQ XXXXX@qq.com
// 102495553@qq.com
// brolly_0204@yahoo.com.cn
// wenli-china@gmail.com
let reg = /^[a-zA-Z0-9\u4E00-\u9FA5]+[a-zA-Z0-9\u4E00-\u9FA5_-]*@[a-zA-Z0-9_]+(\.[a-zA-Z0-9]+){1,2}$/
console.log(reg.test('102495553@qq.com'))
console.log(reg.test('brolly_0204@yahoo.com.cn'))
console.log(reg.test('爱丽丝-china@yahoo.com.cn'))
console.log(reg.test('wenli-china@gmail.com'))
console.log(reg.test('_wenli-china@gmail.com')) // false
console.log(reg.test('wenli-china@gmail')) // false
数据类型检测
// "[Object xxx]"
let reg = /\[Object (\w+)\]/
console.log(reg.exec('[object Array]')[1])
// 数据类型检测
Object.isType = function (val) {
let reg = /^\[object (\w+)\]$/
let res = Object.prototype.toString.call(val)
return reg.exec(res)[1].toLowerCase()
}
console.log(Object.isType(1))
console.log(Object.isType(null))
密码验证
// 包含 数字 字母大小写
// let reg = /^(?!([a-zA-Z]+|\d+)$)[a-zA-Z\d]{8,15}$/
let reg = /^(?!([a-zA-Z]+|[a-z\d]+|[A-Z\d]+)$)[a-zA-Z\d]{8,15}$/
console.log(reg.test('lolABC123'))
正则属性
/**
* 正则实例属性
* flags: 当前正则的修饰符
* global: 是否有g修饰符(是否全局匹配)true 有 false 无
* ignoreCase: 是否有i修饰符 (是否忽略大小写) true 有 false 无
* multiline: 是否有m修饰符 (是否逐行匹配) true 有 false 无
* source: 正则的元字符内容
*/
分组
1.分组捕获
// 1.分组捕获 将大正则划分为小正则 对大正则捕获到的内容进一步进行捕获 放到数组里
var str = '{10}11';
var reg = /\{(\d+)\}/;
console.log(reg.exec(str)); // ["{10}", "10", index: 0, input: "{10}11"]
2.改变优先级
// 2.改变优先级
var reg = /^10|20$/;
console.log(reg.test('10adf'));
console.log(reg.test('adf20'));
console.log(reg.test('10adf20'));
var reg = /^(10|20)$/; // 限定了 只能是10或者20中的一个
console.log(reg.test('10'));
console.log(reg.test('20'));
3.分组引用
// 3.分组引用 (确保分组已经存在 在获取分组引用 否则分组引用操作被忽略)
var reg = /([a-z])([a-z])\1\2/; // 'abab'
var reg = /([a-z])([a-z])\2\1/; // 'abba'
var reg = /([a-z])\1([a-z])\2/; // 'aabb'
var reg = /([a-z])\1([a-z])\2/; // 'aabb'
var reg = /\1(\d)/; // \1要是再第一个分组前面使用 会被忽略掉
console.log(reg.exec('1'));
var reg = /([a-z])\1([a-z])\2/; // 'aabb'
console.log(reg.test('aacc'));
console.log(reg.test('ddff'));
问号
/**
* ?
* 1.量词元字符出现0到1次
* 2.?: 取消分组捕获 只匹配不捕获
* 3.?放在量词元字符后面取消贪婪性 取消贪婪性 就按最短匹配
* 4.?= 正向预查 一个条件 把条件写在分组(?=1) 不会对它进行分组捕获
* 5.?! 反向预查 一个条件 把条件写在分组(?!1) 不会对它进行分组捕获
*/
取消贪婪性
// 贪婪性 按照最长匹配 能多匹配就多匹配
var str = 'h12345243';
// var reg = /\d+/; // + 等价于 {1,} 最短匹配1 最长不限
// console.log(reg.exec(str));
// var reg = /\d{2,4}/; 按最长匹配
// console.log(reg.exec(str)); // '1234'
// var reg = /\d{2,4}?/; // 取消贪婪性 就按最短匹配
// console.log(reg.exec(str)); // '12'
取消分组捕获
// 默认情况只要你进行分组 捕获的时候 都会进行分组捕获
// ?: 取消分组捕获 写在分组的最前面 只匹配该分组 不会进行分组捕获
var reg1 = /^[+-]?(?:\d|[1-9]\d+)(?:\.\d+)?$/g;
console.log(reg1.exec('-123.666'));
正向预查和负向预查
// 正向预查 限定条件
// var reg = /[a-z](?=3|4)/; // 捕获的是字母后面跟着3或4的那个字母
// console.log(reg.exec('a3'));
// console.log(reg.exec('b4'));
// console.log(reg.exec('c3'));
// console.log(reg.exec('d6'));
// 反向预查 排除条件
// var reg = /[a-z](?!3|4)/; // 捕获的是字母后面跟着不是3或4的那个字母
// console.log(reg.exec('a3'));
// console.log(reg.exec('b4'));
// console.log(reg.exec('c3'));
// console.log(reg.exec('d6'));
// var reg1 = /[a-z](?!3|4)/g;
// var str = 'a3b4c6h7';
// console.log(str.match(reg1));
// var reg = /^(?=3)\d$/; // 匹配的一位数只能是3
// console.log(reg.test('3'));
// console.log(reg.test('4'));
// console.log(reg.test('5'));
// var reg = /^((?=3)\d)+$/; // 出现的纯数字 必须都是3
// console.log(reg.test('3'));
// console.log(reg.test('333333'));
// console.log(reg.test('34567'));
// console.log(reg.test('33345'));
// 匹配没有4的手机号
let reg = /^1((?!4)\d){10}$/
console.log(reg.test('15311199201'))
// var reg = /^(?!3)\d$/; // 匹配的一位数不能是3
// console.log(reg.test('3')); // false
// console.log(reg.test('4')); // true
// console.log(reg.test('5')); // true
// var reg = /^((?!3)\d)+$/; // 出现的纯数字 不能有3
// console.log(reg.test('3'));
// console.log(reg.test('4568'));
// console.log(reg.test('4567'));
// console.log(reg.test('33345'));
// var reg = /^(?!a)[a-z]$/; // 限定后面的范围a-z 出现的一个字母不能是a
// console.log(reg.test('a'));
// console.log(reg.test('b'));
// console.log(reg.test('v'));
replace
交换字符串中的两个单词
var re = /(\w+)\s(\w+)/;
var str = "John Smith";
var newstr = str.replace(re, "$2, $1");
//"5=a,6=b,7=c"换成"a=5,b=6,c=7"
var str="5=a,6=b,7=c";
str=str.replace(/(\d+)=(\w)/g,"$2=$1");
console.log(str);
ES6 模板字符串简单实现
let name = 'wenli'
let country = 'china'
let str = `my name is ${name}, I like ${country}`
str = str.replace(/${(.*)}/, (a, b) => {
return eval(b)
})
console.log(str)
vue 模板数据简单实现
let data = {
title: 'hello zhufeng'
}
let str = '<h3>{{ title }}</h3>'
let reg = /{{(.*)}}/
str = str.replace(reg, function(a, b) {
return data[b.trim()]
})
console.log(str)
// console.log(document.body.innerHTML);
// console.log(oText.innerHTML);
// var mess = 'hello world!';
// var say = ' beijinghuanyingni';
// oText.innerHTML = oText.innerHTML.replace(/\{\{(.+?)\}\}/g, function (a, b) {
// console.log(arguments);
// return eval(b)
// });
// replace 支持正则
// var str = 'hh7h8';
// str = str.replace(/1/g, '2');
// str = str.replace(/\d/g, function (a) {
// console.log(arguments);
// // 第一个参数 匹配到的内容
// // 第二个参数 捕获到的索引位置
// // 第三个原始字符串
//// return 2 * arguments[0]
// console.log(a);
// return 2 * a
// });
// console.log(str);
// var str = '{01} -- {02} -- {03} -- {04}';
// var reg = /\{(\d)(\d)\}/g;
// str = str.replace(reg, function (a, b, c) { // 先把根据规则捕获 然后再替换
//// console.log(arguments);
//// console.log(b);
//// 通过形参来得到捕获到的内容
//
// // 第一项是大正则捕获到内容
// // 从第二项开始是分组捕获到的内容
// // 捕获到的索引位置
// // 最后一个原始字符串
// return c + '' + b // 返回值 替换大正则捕获到的内容
// });
// console.log(str);
// str = str.replace(reg, '$2$1'); // $1第一个分组里捕获的内容$1-9
// console.log(str);
// var str = 'zhufeng123zhufeng666';
// var reg = /(\d+)/g;
//
// console.log(reg.exec(str)); // ["123", "123", index: 7, input: "zhufeng123zhufeng666"]
// console.log(reg.exec(str)); // ["666", "666", index: 17, input: "zhufeng123zhufeng666"]
// console.log(RegExp.$1); // 获取到最近一次正则捕获到的分组内容 $1-$9
// 2017-01-01 18:00:40 => 2017年01月01日 18时00分40秒
// 1.方式一
// var str = '2018-05-12 18:00:40';
// var reg = /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/;
// str = str.replace(reg, function (a, n1, n2, n3, n4, n5, n6) {
// console.log(arguments);
// return n1 + '年' + n2 + '月' + n3 + '日 ' + n4 + '时' + n5 + '分' + n6 + '秒'
// })
// console.log(str);
// str = str.replace(reg, '$1年$2月$3日 $4时$5分$6秒');
// console.log(str);
//
// 2. 方式二
// var time = ['年', '月', '日', '时', '分', '秒'];
// var n = 0; // 记录索引
// var reg = /(\d+)[:-]?/g;
// var str = '2018-05-12 18:00:40';
// str = str.replace(reg, function (a, b) {
// // 2018- 2018
//// console.log(b + time[n++]); // 2018 + time[0] 2018年
// return b + time[n++];
// });
// console.log(str);
// 3.方式三
// var time = ['年', '月', '日', '时', '分', '秒'];
// var n = 0; // 记录索引
// var str = '2018-05-12 18:00:40';
//// str.split(/[- :]/) // split 支持正则
//// console.log(str.split(/ /));
//// console.log(str.split(/\s/));
//
//// console.log(str.split(/[- :]/)); // ["2018", "05", "12", "18", "00", "40"]
// var arr = str.split(/[- :]/);
// str = str.replace(/\d+[-:]?/g, function () {
//// 2018- => arr[0]+''+time[0] 05- => arr[1]+ '' +time[1]
// var str2 = arr[n] + '' + time[n];
// n++;
// return str2;
// });
// console.log(str);
// 4.模板引擎
// 2017-01-01 18:00:40 => 2017年01月01日 18时00分40秒
// var str = '2018-05-12 18:00:40';
// var tempStr = '{0}年{1}月{2}日 {3}时{4}分{5}秒'; // 模板字符串
// var arr = str.split(/[: -]/); // ["2018", "05", "12", "18", "00", "40"]
// // console.log(arr);
// str = tempStr.replace(/\{(\d+)\}/g, function (a, b) {
// // arr[0] => {0} arr[1] => {1} arr[2] => {2}
// return arr[b];
// });
// console.log(str);
//
// var htmlStr = 'https://www.baidu.com/s?wd=123&id=28&name1=zhufeng';
//
// var htmlStr2 = 'https://www.baidu.com/s?wd=%E7%99%BE%E5%BA%A6&rsv_spt=1&rsv_iqid=0xb67cb0840000f3bc&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&rqlang=cn&tn=baiduhome_pg&rsv_enter=0&oq=vue&rsv_t=6babjh9kMd3aQtUraNwWM0bfzC5lnPlVkwqWVpOc2Ma898jqbZz9%2Byn76adAkylJ47pg&rsv_pq=ca645c620000ffb4';
// {wd: 123, id: 38, name: zhufeng}
// var reg = /([\w-]+)=([\w-]+)/g;
// var obj = {};
// htmlStr.replace(reg, function (a, b, c) {
//// console.log(arguments);
// obj[b] = c;
// });
// console.log(obj);
// var reg = /([^?&=]+)=([^?&=]+)/g;
// var obj = {};
// htmlStr.replace(reg, function () {
// obj[arguments[1]] = arguments[2];
// });
// console.log(obj);
//?wd=123&id=28&name1=zhufeng';
//var str3 = '?wd=123&id=28&name1=zhufeng';
// var reg = /[^?&]+/g;
//console.log(reg.exec(str3)); // "wd=123",
//console.log(reg.exec(str3)); // id=28
//console.log(reg.exec(str3)); // name1=zhufeng
//// 处理查询字符串
// function getUrlParam(url) {
// var obj = {};
// var reg = /([^?&=]+)=([^?&=]+)/g;
// url.replace(reg, function () {
// obj[arguments[1]] = arguments[2]
// });
// return obj;
// }
//console.log(getUrlParam(htmlStr));
//console.log(getUrlParam(htmlStr2));
// 去除首尾空格
// var str = ' class1 class2 ';
// var reg = /^\s+|\s+$/g;
// var reg = /^ +| +$/g;
// str = str.replace(reg,'');
// console.log(str);
//console.log(str.trim());
// var str2 = str.trim(); // 自带的去除首尾空格
// console.log(str2);
//
// String.prototype.myTrim = function () {
// var reg = /^\s+|\s+$/g;
// return this.replace(reg, '');
// };
//
// var str3 = str.myTrim();
// 千分符 2121345465 => 2,121,345,465
// 1
// var str = '2121345465';
//// console.log(str.split('').reverse().join('')); // 5645431212
// var str2 = str.split('').reverse().join(''); // 5645431212
//
// var reg = /(\d{3})/g;
//// str2 = str2.replace(reg, function () {
//// console.log(arguments);
//// return arguments[0] + ','
//// });
// str2 = str2.replace(reg, '$1,');
// console.log(str2.split('').reverse().join('')); // 2,121,345,465
// 2正向预查
var str = '2121345465.12'; // 2,121,345,465.12
var reg = /(\d)(?=(\d{3})+(\.\d+|$))/g;
// str = str.replace(reg, function () { // 三个数字为一组
//// console.log(arguments); // 2 121 345 465 三组
//// console.log(arguments); // 1 345 465 两组
//// console.log(arguments); // 5 465 一组
// return arguments[0] + ',';
// });
// console.log(str);
str = str.replace(reg, '$1,');
console.log(str);
// 统计字符串中字母重复出现次数
let str = 'asdlfkj;;kajsd66sadff1233132'
str = Array.from(str).sort().join('')
let obj = {}
str.replace(/([a-zA-Z])\1*/g, a => {
obj[a[0]] = a.length
})
console.log(obj) // {a: 3, d: 3, f: 3, j: 2, k: 2, …}
// 每个单词首字母大写
let str = 'my name is brolly, hello world!'
let str2 = str.replace(/\b([a-zA-Z])/g, a => a.toUpperCase())
console.log(str2) // My Name Is Brolly, Hello World!