# 编写正则表达式
创建方式有两种
代码语言:javascript复制// 字面量创建方式
let reg1 = /d /;
// 构造函数 参数:元字符字符串;修饰符字符串
let reg2 = new RegExp("\d "); // 注意构造函数中字 符需要转义
# 元字符和修饰符
常用元字符
量词元字符:设置出现的次数
代码语言:javascript复制// * 零到多次
// 一到多次
// ? 零次或一次
// {n} 出现 n 次
// {n,} 出现 n 到多次
// {n,m} 出现 n 到 m 次
特殊元字符:单个或组合在一起代表特殊的含义
代码语言:javascript复制// 转义字符 (普通 <-> 特殊)
// . 除 n 以为的任意字符
// ^ 以哪一个元字符作为开始
// $ 以哪一个元字符作为结束
// n 换行符
// d 0~9 之间的一个数字
// D 非 0~9 之间的一个数字
// w 数字、字母、下划线中的任意一个字符
// W 非数字、字母、下划线中的任意一个字符
// s 一个空白字符(包含空格、制表符、换页符等)
// S 非空白字符
// t 制表符(一个 Tab 键:四个空格)
// b 匹配一个单词的边界
// x|y x 或 y 中的一个字符
// [xyz] x 或 y 或 z 中的一个字符
// [^xy] 除了 x 和 y 外的任意字符
// [a-z] 指定 a-z 范围中的任意字符 [0-9a-zA-Z_] <=> W
// [^a-z] 除 a-z 的任意字符
// () 正则中的分组符号
// (?:) 只匹配不捕获
// (?=) 正向预查
// (?!) 负向预查
普通元字符:代表本身含义
代码语言:javascript复制// /cellinlab/ 匹配 cellinlab
常用修饰符
代码语言:javascript复制// i ingoreCase 忽略单词大小写匹配
// m multiline 可以进行多行匹配
// g global 全局匹配
# 元字符示例
^ $
let reg = /^d/; // 以数字开始
let reg2 = /d$/; // 以数字结束
let reg3 = /d /; // 包含数字即可
let reg4 = /^d $/; // 以数字开始且以数字结尾
// 验证手机号码
let reg5 = /^1d{10}$/;
let reg = /^2.3$/; // 匹配以 2 开头,以 3 结尾,中间是任意字符的
let reg2 = /^2.3$/; // 匹配 '2.3'
let str = "\d";
let strReg = /^\d$/; // 利用 将 特殊元字符 d 转换成普通 字符
console.log(strReg.test(str)); // true
x|y
let reg = /^18|29$/;
console.log(reg.test('18')); // true
console.log(reg.test('29')); // true
console.log(reg.test('129')); // true
console.log(reg.test('189')); // true
console.log(reg.test('1829')); // true
console.log(reg.test('829')); // true
console.log(reg.test('182')); // true
// 直接 x|y 会存在很乱的优先级问题,一般写的时候会伴随着小括号进行分组,因为小括号可以改变优先级
let reg2 = /^(18|29)$/;
console.log(reg.test('18')); // true
console.log(reg.test('29')); // true
console.log(reg.test('129')); // false
console.log(reg.test('189')); // false
console.log(reg.test('1829')); // false
console.log(reg.test('829')); // false
console.log(reg.test('182')); // false
[]
// 1. [] 中出现的字符一般都代表本身的含义
let reg = /^[@ ] $/; //
console.log(reg.test('@@')); // true
console.log(reg.test('@ ')); // true
// 2. [] 中不存在多位数
reg = /^[18]$/;
console.log(reg.test('18')); // false
console.log(reg.test('1')); // true
console.log(reg.test('8')); // true
reg = /^[10-29]$/; // 1 或 0-2 或 9
console.log(reg.test('1')); // true
console.log(reg.test('9')); // true
console.log(reg.test('0')); // true
console.log(reg.test('2')); // true
console.log(reg.test('10')); // false
# 常用正则表达式
验证是否为有效数字
代码语言:javascript复制/**
* 1. 可能出现 - 号,也可能不出现 ( |-)? 或 [ -]?
* 2. 一位 0-9 都可以,多位首位不能为 0 (d|([1-9]d ))
* 3. 小数部分可能有可能没有,如果有的话后面必须有 小数点 数字 (.d )?
*/
let reg = /^( |-)?(d|([1-9]d ))(.d )?$/;
验证密码
代码语言:javascript复制// 数字、字母、下划线 6~16位
let reg = /^w{6,19}$/;
验证真实姓名
代码语言:javascript复制/**
* 1. 汉字 /^[u4E00-u9FA5]$/
* 2. 名字长度 2-10
* 3. 汉译中间可能有 ·
*/
let reg = /^[u4E00-u9FA5]{2,10}(·[u4E00-u9FA5]{2,10}){0,2}$/;
验证邮箱
代码语言:javascript复制/**
* w((-w )|(.w ))*
* 1. 开头是数字、字母、下划线,1 至 多位
* 2. 还可以是 -数字字母下划线 或 .数字字母下划线 整体零到多位
* 邮箱的名字由“数字、字母、下划线、-、.”几部分组成,但是 . 或 - 不能连续出现也不能作为开始
* @[A-Za-z0-9]
* @ 后面紧跟着数字、字母(1-多位)
* ((.|-)[A-Za-z0-9] )*
* 对 @ 后面名字的补充 (.com.cn)
* .[A-Za-z0-9]
* 匹配最后的域名 .数字或字母 (.com .cn .edu)
*/
let reg = /^w((-w )|(.w ))*@[A-Za-z0-9] ((.|-)[A-Za-z0-9] )*.[A-Za-z0-9] $/;
验证身份证号码
代码语言:javascript复制/**
* 1. 一共 18 位
* 2. 最后一位可能是 X (X 表示 10)
* 3. 前六位:省市县 620122
* 4. 中间八位: 年月日 YYYYMMDD
* 5. 最后四位:
* 最后一位 X 或数字
* 倒数第二位 偶数 女 奇数 男
*/
// 小括号分组捕获
let reg = /^(d{6})(d{4})(d{2})(d{2})d{2}(d)(d|X)$/;
reg.exec('620122199805162005');
// 捕获结果是一个数组,包含每一个小分组单独获取的内容
// ["620122199805162005", "620122", "1998", "05", "16", "0", "5", index: 0, input: "620122199805162005", groups: undefined]
# 正则捕获
实现正则捕获的方法
- RegExp.prototype 的方法
- exec
- test
- String.prototype 上支持正则表达式处理的方法
- replace
- match
- split
- 其他
// 实现正则捕获的前提是:当前正则要和字符串匹配,如果不匹配捕获结果为 null
let str = 'cell2019cellinlab2020startup2021';
let reg = /d /;
console.log(reg.exec(str));
// ["2019", index: 4, input: "cell2019cellinlab2020startup2021", groups: undefined]
/**
* exec 实现的正则捕获
* 1. 捕获的结果是 null 或 一个数组
* 第一项:本次捕获的内容
* 其余项:对应小分组本次单独捕获的内容
* index: 当前捕获内容在字符串中的索引
* input: 原始字符串
* 2. 每执行一次 exec 只能捕获一个符合正则规则的,但默认情况始终下执行第一次的捕获(正则捕获的懒惰性,原因是匹配后 lastIndex 值默认不会被修改)
* reg.lastIndex:当前正则下一次匹配的起始索引位置
*/
reg = /d /g;
console.log(reg.lastIndex); // 0
console.log(reg.exec(str));
console.log(reg.lastIndex); // 8
// 实现 execAll 执行一次可以获取所有匹配结果
~ function() {
function execAll(str='') {
if (!this.global) {
return this.exec(str);
}
const arr = [];
let res = this.exec(str);
while(res) {
arr.push(res[0]);
res = this.exec(str);
}
return arr.length === 0 ? null : arr;
}
RegExp.prototype.execAll = execAll;
}();
let reg2 = /d /g;
console.log(reg2.execAll(str)); // ["2019", "2020", "2021"]
console.log(str.match(reg2)); // ["2019", "2020", "2021"]
# 正则捕获的贪婪性
默认安装当前正则匹配的最长结果来获取
代码语言:javascript复制let str = 'cell2020@cellinlab2021';
let reg = /d /g;
console.log(str.match(reg)); // ["2020", "2021"]
reg = /d ?/g; // 在量词元字符后面设置 ? 取消贪婪性(按照正则匹配最短结果来获取)
console.log(str.match(reg)); // ["2", "0", "2", "0", "2", "0", "2", "1"]
# 其他正则捕获方法
test
代码语言:javascript复制let str = '{0}年{1}月{2}日';
let reg = /{(d )}/g;
console.log(reg.test(str)); // true
console.log(RegExp.$1); // 0
console.log(reg.test(str)); // true
console.log(RegExp.$1); // 1
console.log(reg.test(str)); // true
console.log(RegExp.$1); // 2
console.log(reg.test(str)); // false
console.log(RegExp.$1); // 2 存储上一次捕获的结果 支持 $1-$9,即获取前九个分组信息
replace 字符串中实现替换的方法
代码语言:javascript复制let str = 'cell@2019|cell@2020';
// str = str.replace('cell', 'cellinlab').replace('cell', 'cellinlab');
// cellinlabinlab@2019|cell@2020
str.replace(/cell/g, 'cellinlab');
// cellinlab@2019|cellinlab@2020
应用
代码语言:javascript复制// => 2021年03月02日
let time = '2021-03-02';
let reg = /^(d{4})-(d{1,2})-(d{1,2})$/;
// time = time.replace(reg, '$1年$2月$3日'); // 2021年03月02日
// 能捕获几次机会将函数执行几次,函数参数和 exec 捕获的内容一一致
time = time.replace(reg, (big, $1, $2, $3) => {
console.log(big, $1, $2, $3); // 2021-03-02 2021 03 02
return `${$1}年${$2}月${$3}日`;
}); // 2021年03月02日
首字母大写
代码语言:javascript复制let str = "hello world";
let reg = /b([a-zA-Z])[a-zA-Z]*b/g;
str = str.replace(reg, (...arg) => {
let [word, firstLetter] = arg;
firstLetter = firstLetter.toUpperCase();
word = word.substring(1);
return firstLetter word;
}); // "Hello World"
获取字符串中出现最多的字母即次数
代码语言:javascript复制let str = 'cellinlab@copyright by cell';
// 排序
let str1 = str.split('').sort((a, b) => a.localeCompare(b)).join(''); // @abbccceeghiilllllnoprtyy
let reg = /([a-zA-Z])1 /g; // 1 出现和其一样的并且出现多次
let arr = str1.match(reg).sort((a, b) => b.length - a.length); // ["lllll", "ccc", "bb", "ee", "ii", "yy"]
let max = arr[0].length;
let res = [arr[0].substr(0, 1)];
for (let i = 1; i < arr.length; i ) {
let item = arr[i];
if (item.length < max) {
break;
}
res.push(item.substr(0, 1));
}
字符串方法扩展
代码语言:javascript复制~function () {
/**
* 时间字符串格式化
* @param {String} template 模板
* @return 格式化后的时间字符串
*/
function formatTime(template = '{0}年{1}月{2}日 {3}时{4}分{5}秒') {
let timeArr = this.match(/d /g);
template = template.replace(/{(d )}/g, (content, $1) => {
let time = timeArr[$1] || '00';
time.length < 2 ? time = `0${time}` : null;
return time;
});
return template;
}
/**
* queryURLParams 获取 URL 地址问号后面的参数信息,可能包含hash值
*/
function queryURLParams() {
// /([^?=&#] )=([^?=&#] )/g
let obj = {};
this.replace(/([^?=&#] )=([^?=&#] )/g, (...[, $1, $2]) => obj[$1] = $2);
this.replace(/#([^?=&#] )/g, (...[, $1]) => obj['HASH'] = $1);
return obj;
}
/**
* 千分符
*/
function millimeter() {
// /d{1,3}(?=(d{3}) $)/g
return this.replace(/d{1,3}(?=(d{3}) $)/g, (content) => {
return content ',';
});
}
['formatTime', 'queryURLParams', 'millimeter'].forEach(method => {
String.prototype[method] = eval(method);
})
}();
let str = '2021-3-3 9:18:7';
str.formatTime(); // "2021年03月03日 09时18分07秒"
str.formatTime('{1}-{2} {3}:{4}'); // "03-03 09:18"
let url = 'http://api.cellinlab.xyz/?type=log&time=2020#blog';
url.queryURLParams(); // queryURLParams
let num = '89976611'; // 89,976,611
num.millimeter(); // "89,976,611"