进阶教程 6. 正则应用

193 阅读1分钟

这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战

一、正则的简单应用

1. 验证中国大陆手机号 以1开头的11位数字

let reg1 = /^1\d{10}$/;
console.log(reg1.test('16601046931')); // true

2. 验证某些号段的手机号验证,例如188 170 156

let reg2 = /^1(88|70|56)\d{8}$/;
console.log(reg2.test('18801046931')); // true

3. 匹配尾号是连续的三围豹子号 666 777 111

let reg3 = /^1\d{7}(\d)(\1{2})$/;
console.log(reg3.test('15912345666')); // true

4. 靓号 中间四位相同后面四位相同

let reg5 = /^1\d{2}(\d)\1{3}(\d)\2{3}$/;
console.log(reg5.test('17733332222'));

5. 有效数字验证:

10 +10 -10 0.12 10.02

  • +或-最多出现一次
  • 如果是一位数字可以是0-9
  • 多位数不能以0开头
  • 如果有小数点,后面必须跟至少一位小数
let reg6 = /^[+-]?(\d|[1-9]\d+)(.\d+)?$/;

5. 验证数字在某个范围内 18 - 60

  • 18 - 19 1[89]|
  • 20-59 2[0-9]
  • 60 60
let reg7 = /^(1[89]|[2-5]\d|60)$/;
console.log(reg7.test('17')); // true
console.log(reg7.test('18')); // true
console.log(reg7.test('19')); // true
console.log(reg7.test('59')); // true
console.log(reg7.test('60')); // true
console.log(reg7.test('61')); // true

6. 匹配中文姓名

  • 中文的Unicode编码范围 \u4e00-\u9fa5
  • 出现2位到6位
let reg8 = /^[\u4d00-\u9fa5]{2,6}$/;
console.log(reg8.test('qq')); // true

6. 邮箱验证(简单验证)

let reg9 = /^[a-zA-Z\d]+@[a-zA-Z\d]+(.[a-z]+){1,2}$/;
console.log(reg9.test('rmbbingo@163.com')); // true
console.log(reg9.test('rmbbingo@163@.com')); // false
console.log(reg9.test('1164664451@qq.com')); // true
console.log(reg9.test('1164664451@sina.com.cn')); // true
console.log(reg9.test('1164664451@sina.com.cn.cn')); // false

7. 数据类型检测

Object.isTypeOf = function (val) {
   let res = Object.prototype.toString.call(val); // "[object Xxxx]"
   let reg = /^[object ([a-zA-Z]+)]$/; // 在正则使用某些特殊元字符的原义时需要转义
   let exec = reg.exec(res)[1];
   return exec.toLowerCase();
};
console.log(Object.isTypeOf(1));

二、正向预查和负向预查

  • 正向预查:(?=元字符) 不发生分组捕获
  • 负向预查: (?!元字符) 不发生分组捕获

1 正向预查

/x(?=y)/ 匹配x,仅当x后面紧跟着y(匹配后面跟着y的x);

let reg1 = /[a-z](?=3|4)/g;
let str1 = 'a1 b2 c3 d4';
console.log(str1.match(reg1)); // ["c", "d"]

2负向预查

/x(?!y)/ 匹配x,仅当x后面不是y(匹配后面不是y的x);

let reg2 = /[a-z](?!3|4)/g;
console.log(str1.match(reg2));

3. 示例

有个经典的应用正则的正向预查能力的例子——给数字加上千分符,例如数字1234567890 加千分符后,1,234,567,890。 这个 case 用正则处理会十分简便:

var reg = /(\d)(?=(\d{3})+$)/g;
var str = '1234567890';
var str1 = str.replace(reg, '$1,');
console.log(str1);

需要说明的是,代码中 replace(reg, (whole, g1) => g1 + ',') 是用来访问 js 正则的分组的,其中 1 表示第一个分组,这一部分内容将在下文正则不活中详述。

当然也可以用回调函数的形式接受分组,回调函数从第二个参数开始表示分组,如上文所述。如果用回调,则replace(reg, (whole, g1) => g1 + ',')

三、正则捕获

js正则捕获:把符合某个规则的字符串获取到;

+常见的捕获方法:

RegExp.prototype.exec(str);
String.prototype.match();

3.1 懒惰性

let reg = /\d+/;
let str = 'hello111 hello222 hello333';

console.log(reg.exec(str)); ['111', index.....]
console.log(reg.exec(str)); ['111', index.....]
console.log(reg.exec(str)); ['111', index.....]
console.log(reg.lastIndex); 0

console.log(str.match(reg)); ['1111']
console.log(str.match(reg)); ['1111']
console.log(str.match(reg)); ['1111']

3.2 取消懒惰性:给正则增加修饰g

let reg2 = /\d+/g;
let str2 = 'hello111 hello222 hello333';

console.log(reg2.exec(str2));
console.log(reg2.exec(str2));
console.log(reg2.exec(str2));
console.log(reg2.lastIndex);

console.log(str2.match(str2));

3.3 贪婪性:按照匹配最多的去捕获

解决贪婪性:给量词元字符后面加 ?

3.4 分组捕获:

使用小括号表示正则分组

let reg3 = /hello(\d+)/g;
console.log(reg3.exec(str)[1]);
console.log(reg3.exec(str)[1]);
console.log(reg3.exec(str)[1]);
  • 除了通过[索引]访问分组,在RegExp上有几个属性记录正则分组捕获的内容
  • RegExp.1表示第1个分组的捕获内容;RegExp上有1 表示第1个分组的捕获内容; 在RegExp上有 1-$9表示第1到第9个分组内容
  • RegExp['$&'] 表示整个正则捕获到的内容
console.log(RegExp.$1);
console.log(RegExp['$&']);

九、正则的高级应用

1. 配合replace使用

String.prototype.replace(匹配内容, '替换内容');

let str = 'qq2018 qq2019';
  • 把字符串中的 qq 替换成 江外
let result = str.replace('qq', '江外');
console.log(result); // 江外2018 qq2019
  • 一次只能替换一个,那么有没有什么办法一次都替换掉呢?

replace(正则, callback); replace配合正则使用时,可以实现全部替换,或者安装某种规则遍历字符串

回调函数的参数:

  1. 第一个是整个正则匹配到的内容
  2. 如果有分组,会从第二个参数
  3. 倒数第二个参数是 本次捕获的的起始索引位置
  4. 最后一个参数是当前原始字符串
let i = 0;
let reg = /(qq)/g;
let r2 = str.replace(reg, function (...arg) {
   // 第一个整个正则匹配到的内容
   // 如果有分组,会从第二个参数
   // 倒数第二个参数是 本次捕获的的起始索引位置
   // 最后一个参数是当前原始字符串
   i++;
   console.log(arg);
   return '江外'
});
console.log(r2);
console.log(i); // 2 说明回调函数执行了2次;

2. 格式化时间字符串

按照时间模板格式时间字符串

  • "2019/6/30 17:50:23" => '06-30 17:50'

  • 2.1 获取时间字符串中所有时间数字

let str2 = "2019/6/30 17:50:23";
let reg2 = /\d+/g;
let ary = str2.match(reg2);
console.log(ary);
  • 2.2 判断如果这些数字比10小,需要补0
let addZero = ary.map(item => +item < 10 ? '0' + item : item);
console.log(addZero);
  • 2.3 根据模板把时间格式化
let tmp = '{0}年{1}月{2}日 {3}时{4}分{5}秒';
let tmpReg = /{(\d)}/g;
let result2 = tmp.replace(tmpReg, function ([, index]) {
   return ary[index];
});
console.log(result2);
  • 扩展到原型上
String.prototype.myFormatTime = function (tmp = '{0}年{1}月{2}日 {3}时{4}分{5}秒') {
   let ary = this.match(/\d+/g).map(item => +item < 10 ? '0' + item : item);
   return tmp.replace(/{(\d)}/g, (...[, index]) => ary[index]);
};

console.log("2018/4/30 17:50:23".myFormatTime('{1}-{2} {3}:{4}:{5}'));

3. url查询参数格式化

let url = 'http://www.qq.cn?name=rmb&age=18&form=qq&addre=hebei';
  • 把上面的url参数的转换成一个对象:{name: 'rmb', age: '18'}
let params = {};
let reg3 = /([^?=&]+)=([^?=&]+)/g;
url.replace(reg3, (str, key, value) => params[key] = value);
console.log(params);
  • 扩展到原型上
String.prototype.urlSeries = function () {
   let params = {};
   let reg3 = /([^?=&]+)=([^?=&]+)/g;
   this.replace(reg3, (str, key, value) => params[key] = value);
   return params
};
let result3 = url.urlSeries();
console.log(result3);

4. 获取数据类型

Object.isTypeOf = function (val) {
   let res = Object.prototype.toString.call(val); // "[object Xxxx]"
   let reg = /^[object ([a-zA-Z]+)]$/; // 在正则使用某些特殊元字符的原义时需要转义
   let exec = reg.exec(res)[1];
   return exec.toLowerCase();
};
console.log(Object.isTypeOf(1));