# 编写正则表达式
创建方式有两种
// 字面量创建方式 let reg1 = /\d+/;
// 构造函数 参数:元字符字符串;修饰符字符串
let reg2 = new RegExp("\d+"); // 注意构造函数中字 符需要转义
# 元字符和修饰符
常用元字符
量词元字符:设置出现的次数
// * 零到多次
// + 一到多次
// ? 零次或一次
// {n} 出现 n 次
// {n,} 出现 n 到多次
// {n,m} 出现 n 到 m 次
特殊元字符:单个或组合在一起代表特殊的含义
// \ 转义字符 (普通 <-> 特殊) // . 除 \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 的任意字符
// () 正则中的分组符号
// (?:) 只匹配不捕获
// (?=) 正向预查
// (?!) 负向预查
普通元字符:代表本身含义
// /cellinlab/ 匹配 cellinlab
常用修饰符
// i ingoreCase 忽略单词大小写匹配
// m multiline 可以进行多行匹配
// g global 全局匹配
# 元字符示例
^ $
let reg = /^\d/; // 以数字开始
let reg2 = /\d$/; // 以数字结束
let reg3 = /\d+/; // 包含数字即可
let reg4 = /^\d+$/; // 以数字开始且以数字结尾
// 验证手机号码
let reg5 = /^1\d{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
# 常用正则表达式
验证是否为有效数字
/**
* 1. 可能出现 + - 号,也可能不出现 (+|-)? 或 [+-]?
* 2. 一位 0-9 都可以,多位首位不能为 0 (\d|([1-9]\d+))
* 3. 小数部分可能有可能没有,如果有的话后面必须有 小数点 + 数字 (\.\d+)?
*/
let reg = /^(+|-)?(\d|([1-9]\d+))(\.\d+)?$/;
验证密码
// 数字、字母、下划线 6~16位
let reg = /^\w{6,19}$/;
验证真实姓名
/**
* 1. 汉字 /^[\u4E00-\u9FA5]$/
* 2. 名字长度 2-10
* 3. 汉译中间可能有 ·
*/
let reg = /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{2,10}){0,2}$/;
验证邮箱
/**
* \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]+$/;
验证身份证号码
/**
* 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"]
# 正则捕获的贪婪性
默认安装当前正则匹配的最长结果来获取
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
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 字符串中实现替换的方法
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
应用
// => 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日
首字母大写
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"
获取字符串中出现最多的字母即次数
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));
}
字符串方法扩展
~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"