正则表达式专题 常见的正则方法及元字符等

正则表达式专题

邂逅正则表达式

正则表达式基础

/*
 * @Author: 毛毛
 * @Date: 2022-03-06 12:18:14
 * @Last Modified by: 毛毛
 * @Last Modified time: 2022-03-07 09:33:48
 */
/* 正则表达式 ->
        1. 只能处理字符串
        2. 是一个规则:可以验证字符串是否符合某个规则(test)
            也可以吧字符串中符合规则的内容捕获到(exec / match...)
*/
let str = "good good study, day day up!";
// 创建一个正则
/**
 *  \d => 0-9的数字
 *  + => 出现一次或任意多次
 */
let reg = /\d+/;
console.log(reg.test(str))
str = "2019-08-12";
const arr = reg.exec(str);
console.log(arr) // ['2019', index: 0, input: '2019-08-12', groups: undefined]


/*
  正则创建方式:
    1. /写正则规则/
    2. new RegExp(元字符字符串, 修饰符字符串)
      正则的组成:
        @1. 元字符: 定义当前正则的规则
          常用的原字符:
            1): 量词元字符:设置出现的次数 6个
              a) * 表示零到多次
              b) + 一到多次
              c) ? 零次 或者 一次
              d) {n} 出现 n次
              e) {n,} 出现 n 及 n+ 次
              f) {n,m} 出现 n 到 m 次
              
            2) 特殊元字符:单个或者组合在一起代表特殊的含义 20个
              单个元字符
              a) \ 转义字符 (普通 -> 特殊 -> 普通)
              b) . 除 \n(换行符) 以外的任意字符
              c) ^ 以 哪一个元字符作为开始
              d) $ 以 那个元字符作为结束
              // --------------------------------
              组合元字符
              e) \n 换行符  \1 后面出现和前面匹配的内容一样 且出现多次
              f) \d 0-9之间的数字 , \D 非 0-9之间的数字
              g) \w 数字 字母 下划线 中的任意一个字符 , \W 非 数字 字母 下划线 中的任意一个字符
              h) \s 一个空白字符(空格 制表符tab 换页符等) \t 一个制表符 (tab: 四个空格)
              i) \b 字符边界 \B 取反
              // ----------------------------------------
              j) x|y  x或者y中的一个字符 
              k) [abc]  a或b或c中的任意一个字符 
              l) [^abc] 非a或b或c中的任意一个字符 
              m) [0-9a-z] 指定某个范围中的任意字符
              n) [^0-9a-z] 非指定某个范围中的任意字符
              o) () 正则中的分组符号
              p) (?:) 只匹配 不捕获
              q) (?=) 正向预查
              r) (?!) 负向预查
              // ---------------------------------------------
              普通元字符
              a) /你好/ 匹配的就是 你好 两个字符
        @2. 修饰符: 
          常用的修饰符
            a) i 忽略大小写匹配 ignoreCase
            b) m 忽略换行 进行多行匹配 multiline
            c) g 全局匹配  global
*/
reg = new RegExp("1从"); // 需要转义 因为是普通字符串 \\ => 真正的斜杠 \
// console.log(reg.test(",122"));

元字符解析

// 数字开始
let reg = /^\d/;
// console.log(reg.test("12a"))
// console.log(reg.test("a21"))

// 以字符结尾
reg = /[a-zA-Z]$/;
// console.log(reg.test("12a"))
// console.log(reg.test("aa1"))
// 包含数字 就符合要求
reg = /\d/;
// console.log(reg.test("a1"))
// console.log(reg.test("aa1"))

// 字符串 只能是 一个数字
reg = /^\d$/;
// console.log(reg.test("1"))
// console.log(reg.test("1a"))
// console.log(reg.test("a1a"))

// 验证手机号码: 11位
reg = /^1\d{10}$/;
// console.log(reg.test("11111111111"))
// console.log(reg.test("111111111112"))
// console.log(reg.test("21111111112"))

// 匹配小数
reg = /^\d+\.\d+$/;
// console.log(reg.test("1.2"))
// console.log(reg.test("22"))

// 转义字符
reg = /^\\d$/;
// console.log(reg.test("\\\d")) // 字符串出现转义字符 也有问题
// console.log(reg.test("\\d")) // 字符串出现转义字符 也有问题

// x|y
reg = /^12|34$/;
// console.log(reg.test("12"))
// console.log(reg.test("34"))
// console.log(reg.test("1"))// false
// console.log(reg.test("123"))
// console.log(reg.test("124"))
// console.log(reg.test("234"))
// console.log(reg.test("1234"))
// x|y的规则很乱 优先级不好说
// 一般使用 x|y 都会使用 () 进行分组 改变优先级
reg = /^(12|34)$/; // 12 | 34 之间的任意一个
// console.log(reg.test("12"))
// console.log(reg.test("34"))
// console.log(reg.test("1234"))

// [] 中括号中出现的字符 一般代表本身的含义
reg = /^[@+]+$/;
// console.log(reg.test("@")) // 全是 true
// console.log(reg.test("@+"))
// console.log(reg.test("@@@"))
// console.log(reg.test("@++"))
// console.log(reg.test("@@@@++"))
// console.log(reg.test("++++"))

// reg = /^[\d]$/; 匹配数字
// console.log(reg.test("d"))// false
// console.log(reg.test("\\"))//false
// console.log(reg.test("\\d"))//false

reg = /^[\\d]$/; //匹配 \ 或者 d
// console.log(reg.test("d"))// true
// console.log(reg.test("\\"))// true
// console.log(reg.test("\d")) // true
// console.log(reg.test("\\d")) // false

// 中括号不存在多位数
reg = /^[10-29]$/ ; //=> 表示 1 0-2 9 中的任意一个数字
console.log(reg.test("1"))
console.log(reg.test("0"))
console.log(reg.test("9"))
console.log(reg.test("2"))
console.log(reg.test("10"))

常用正则表达式

// 1. 有效数字
let reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/;
// console.log(reg.test("12"))
// console.log(reg.test("+12"))
// console.log(reg.test("-12"))
// console.log(reg.test("-12."))
// console.log(reg.test("-12.0"))
// console.log(reg.test(".1"))
// console.log(reg.test("0.1"))
// console.log(reg.test("01"))
// console.log(reg.test("01.2"))

// 密码 6-16位 数字 字母 下划线
// reg = /^\w{6,16}$/;
// console.log(reg.test("123456"))
// console.log(reg.test("123456_"))
// console.log(reg.test("123456_aw"))
// console.log(reg.test("123456_000000000000000"))

// 验证姓名 中文汉字的正则   2-12
// 汉字 [\u4E00-\u9FA5]
// 译名 你好·世界
reg = /^[\u4E00-\u9FA5]{2,12}(·[\u4E00-\u9FA5]{2,12}){0,2}$/;

// 验证邮箱
/*
  1. 开头 数字字母下划线
  2. - . 不能连续出现 整体出现 零到多次
  3. @后面 是数字字母 1到多次
  4. ((\.|-)[A-Za-z0-9]+)* 匹配对@后面名字的补充 也就是多域名 @ss.com.cn
  5. .com .cn 匹配域名 \.[A-Za-z0-9]+
*/
reg = /^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/;


// 身份证 
/*
  1. 18位
  2. 最后一位可能是 X
  3. 前六位是所在是省市县
  4. 中间八位是出生年月日
  5. 最后四位:
    最后一位是 X 或者 数字
    倒数第二位 偶数 女 奇数 男
    其余是经过算法算出来的
*/
// 小括号的第二个作用  分组捕获,不仅可以匹配大正则的信息 还可以单独匹配每个小分组的内容
reg = /^(\d{6})(\d{4})(\d{2})(\d{2})(\d{2})(\d)(\d|X)$/;
console.log(reg.exec("41152421000101123X"))

两种创建方式的区别

// 构造函数创建 因为传递是字符串 需要使用两个 \\ 才代表一个斜杠
// new RegExp("\\d") //=> /\d/

// 正则表达式中的部分内容是变量存储的值
// 1. 两个斜杠中间包起来的都是元字符
let type = "mao";
let reg = /^@"+type+"@$/;
console.log(reg) // /^@"+type+"@$/ 无法拼接变量的值

// 也就是说 正则中如果要包含某个变量的值 则不能使用字面量方式创建
// 需要拼接变量的话 需要使用 构造方式 拼接变量的值
reg = new RegExp("^@"+type+"@$")
console.log(reg) // /^@mao@$/

正则表达式 捕获的懒惰性

/*
 * @Author: 毛毛 
 * @Date: 2022-03-06 20:07:21 
 * @Last Modified by: 毛毛
 * @Last Modified time: 2022-03-06 21:01:31
 */
/*
  实现正则的捕获:
    1. RegExp.property的方法
      @1. exec
      @2. test
    2. 字符串String.property上支持正则表达式处理的方法
      @1. replace
      @2. match
      @3. split
    
    实现正则捕获的前提是 字符串的内容需要有和正则匹配的
*/
// 基于 exec方法 实现正则的捕获:
// 结果是 null 或者是 一个数组
let str = "mao2022jun2021";
let reg = /\d+/;
/*
  ['2022', index: 3, input: 'mao2022jun2021', groups: undefined]
    数组第一项的内容是:本次正则捕获到的内容
        其余项内容:如 1 2 3索引对应的内容 会是小分组本次单独捕获到的内容
    index:当前捕获内容在字符串中的起始索引
    input:原始字符串
  每次执行一次 exec 只能捕获到一个符合正则规则的,
  但是默认情况下:我们执行一百遍 获取的结果永远都是第一个匹配到的内容,
  其余的捕获不到。
  => 这就是正则捕获的懒惰性: 默认只捕获第一个
  reg.lastIndex : 当前正则下一次匹配的起始索引位置
    懒惰性的原因: 默认lastIndex的值不会被修改,
      每一次都是从字符串起始位置查找,所以找到的永远都是第一个。
    解决方式: 修饰符修改为全局匹配 g
  注意:基于 test方法 进行正则匹配以后 lastIndex的值也会发生改变(基于全局修饰符g)
*/
// console.log(reg.exec(str)); // ['2022', index: 3, input: 'mao2022jun2021', groups: undefined]
// console.log(reg.exec(str));

// 设置全局修饰符 每次匹配完毕后 lastIndex会自动改变
// 没有捕获到了以后 lastIndex重置 下次捕获会从头继续开始
// TODO 注意:手动修改 lastIndex的值是无效的
// reg = /\d+/g;
// console.log(reg.exec(str), reg.lastIndex);
// console.log(reg.exec(str), reg.lastIndex);
// console.log(reg.exec(str), reg.lastIndex); // 没有捕获到了以后 lastIndex重置 下次捕获会从头继续开始
// console.log(reg.exec(str), reg.lastIndex); 

// 需求 编写一个方法 execAll 执行一次 可以把所有的匹配结果给捕获到(g全局修饰符)
/**
 * 执行一次 可以把所有的匹配结果给捕获到
 * 正则表达式需要满足修饰符 全局匹配 否则会形成死循环
 * @param {*} str 字符串
 * @returns 所有匹配的结果
 */
RegExp.prototype.execAll = function execAll(str) {
  // 不是全局正则匹配 只捕获一次
  if(!this.global) return this.exec(str);
  // str: 需要匹配的字符串
  // this: 当前正则表达式
  let ary = [], res; // ary 存储所有捕获的结果 res 每次捕获的结果
  while (res = this.exec(str)) {
    ary.push(res[0]);
  }
  return ary.length ? ary : null;
}
reg = /\d+/g;
console.log(reg.execAll("mao2021jun2022mao2023")); // ['2021', '2022', '2023']

// 我们编写的 execAll方法 和字符串的match方法的返回值结果一致
// 正则表达式 虽然没有直接捕获所有匹配的结果的方法 但是 字符串有 但是正则也需要设置全局匹配
console.log("mao2021jun2022mao2023".match(/\d+/g)) // ['2021', '2022', '2023']

分组捕获 和分组引用

/*
 * @Author: 毛毛 
 * @Date: 2022-03-06 21:04:56 
 * @Last Modified by: 毛毛
 * @Last Modified time: 2022-03-06 21:56:09
 */
/*
  分组的作用:1 提高优先级 2 分组捕获 3 分组引用
  分组捕获:
    身份证号码
*/
let str = "42123120001205563X";
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})(\d{2})(\d)(\d|X)$/;
// 如下两个结果 一致 数组第一项:大正则匹配的结果
// 后面的其余数组项: 每个小分组单独匹配捕获的结果
// console.log(reg.exec(str))
// console.log(str.match(reg))

// 如果使用括号 只是为了提高优先级 不是为了捕获 可以使用 (?:正则) 只匹配不捕获
reg = /^(\d{6})(\d{4})(\d{2})(\d{2})(\d{2})(?:\d)(?:\d|X)$/;
// console.log(reg.exec(str))
// console.log(str.match(reg))

// 纪要捕获到 {数字} 也想单独的把数字获取到 例如:第一次找到 {0} 还需要单独获取到 0
str = "{0}年{1}月{2}日";
// 使用大正则 + 小分组
reg = /\{(\d+)\}/g;
console.log(reg.exec(str))
console.log(str.match(reg)) // 多次匹配的情况下,match只能获取大正则匹配的内容
RegExp.prototype.execAllGroup = function (str) {
  const arrBig = []; // 大正则匹配结果
  const arrSmall = [];// 小正则匹配结果
  let res;
  while (res = this.exec(str)) {
    const [big, small] = res;
    arrBig.push(big);
    arrSmall.push(small);
  }
  return [arrBig, arrSmall];
}
reg = /\{(\d+)\}/g;
console.log(reg.execAllGroup(str));

/*
  分组的第三作用: 分组引用
    是通过 \数字 让其代表和对应分组出现一模一样的内容
    需要匹配同时出现某些相同的内容时 使用分组引用
*/
str = "book"; // good look 两个一样的字符同时出现 
// 匹配四个字符的字符串 中间两个字符相同的
reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/;
console.log(reg.exec(str))
// console.log(reg.exec("week"))
console.log(reg.exec("deep"))
console.log(reg.exec("deeep")) // null
console.log(reg.exec("dep")) // null
console.log(reg.exec("depp")) // null

取消贪婪性

/*
 * @Author: 毛毛 
 * @Date: 2022-03-06 22:04:49 
 * @Last Modified by: 毛毛
 * @Last Modified time: 2022-03-06 22:18:14
 */
/*
  正则捕获的贪婪性:
    默认情况下:正则捕获的时候,是按照当前正则匹配的最长结果来获取的
  取消贪婪性:
    以最短匹配结果捕获 在量词元字符后面设置 ? 

  注意:问号的五大作用
    问号左边是非量词元字符:本身代表量词元字符,出现 零到多次
    问号左边是量词元字符: 取消正则捕获贪婪性
    (?:) 只匹配不捕获
    (?=) 正向预查
    (?!) 负向预查
*/
let str = "mao@1234@4321mao";
// 匹配的数字 只要符合 同时捕获为最长的字符串
let reg = /\d+/g;
console.log(str.match(reg));

// 取消正则捕获贪婪性 以最短匹配结果捕获 在量词元字符后面设置 ? 
reg = /\d+?/g;
console.log(str.match(reg))

其他正则捕获的方法

/*
 * @Author: 毛毛 
 * @Date: 2022-03-07 08:24:05 
 * @Last Modified by: 毛毛
 * @Last Modified time: 2022-03-07 09:14:27
 */

/*
  1. test 方法 也能捕获 (本意是匹配)【了解即可】
    使用该方法后  通过 RegExp.$1 获取第一个分组的内容 最多到 $9
    也就是说最多支持9个分组的信息 且只有 test的匹配结果是true,值才会更新
*/
let str = "{0}年{1}月{2}日";
let reg = /\{(\d+)\}/g;
console.log(reg.test(str), RegExp.$1)

/*
  2. replace 字符串的替换方法 一般伴随正则一起使用
    不使用正则或者不进行全局正则匹配,进行替换的时候每次都是从字符串的第一个字符开始搜索替换
    使用全局正则匹配,会取消正则懒惰性,每次lastIndex都会发生改变 不会每次回到第一个字符查找
*/
str = "mao@2020||mao@2022"
console.log(str.replace(/[a-zA-Z]+/g, "maomao"));

let time = "2022-03-07";
// 把字符串转为 xxxx年xx月xx日
reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
/*
  $1年$2日$3日:
    $1 $2 这些 都是 exec方法每次执行的结果中的小正则分组匹配到的结果
  replace方法接收的参数 (正则,函数)
  1. replace方法不仅把方法执行(能匹配几次 就执行几次函数,而且是
    匹配一次,就立刻执行一次函数),
  2. 还给方法传递了实参信息【就是exec的结果】(大正则的内容,后面是依次的小正则匹配内容)
  3. 在函数中我们返回的是什么 就把当前大正则的匹配内容替换为啥
  
*/
time = time.replace(reg, "$1年$2日$3日");
console.log(time) // 2022年03日07日
time = "2022-03-07".replace(reg, (big, $1, $2, $3) => {
  // big $1, $2, $3 等都是自己设置的变量
  // console.log(big, $1, $2, $3)
  return `${$1}${$2}${$3}`
});
console.log(time)

time = "2022-3-7".replace(reg, (big, ...args) => {
  let [$1, $2, $3] = args;
  $2 = $2.padStart(2, "0");
  $3 = $3.padStart(2, "0");
  return `${$1}${$2}${$3}`
});
console.log(time)

// 单词首字符大写
str = "good good study, day day up!";
// \b 匹配字符边界
reg = /\b([a-zA-Z])[a-zA-Z]*\b/g;
console.log(str.replace(reg, (big, ...args) => {
  return args[0].toUpperCase() + big.substring(1)
}))

正则练习

出现最多的字符

let str = "dasujh,akdbasdbaisdimoisadja,,adiasdab adsads";
// 找到字符串中 那个字符出现的次数最多
/*
  1. 去重
*/
// let res = {}
// Array.prototype.forEach.call(str, item=>{
//   if(typeof res[item] !== "undefined") res[item]++;
//   else res[item] = 1;
//   // console.log(item)
// })
// console.log(res)

/*
  2. 字符串排序
*/
const res = str.split("").sort((a, b) => {
  return a.localeCompare(b);
})
reg = /([a-zA-Z])\1+/g;
// console.log(res.join("").match(reg).sort((a,b)=>b.length-a.length)[0].slice(0,1))

/*
  3. 排序后使用正则表达式
*/
str = res.join("")
let result = [], max = 0, flag = false;
for (let i = str.length; i > 0; i--) {
  r = new RegExp("([a-zA-Z])\\1{" + (i - 1) + "}", "g");
  str.replace(r, (big, $1) => {
    result.push($1);
    max = i;
    flag = true;
  });
  if (flag) break;
}
console.log(result, max)

时间字符串格式化

// 转为 指定格式的时间
/**
 * 
 * @param {*} template 模板 期望的日期格式
 *    模板规则: {0} 年 ...{1-5}月日时分秒
 * @returns 格式化好的时间字符串
 */
function formatTime(template = "{0}年{1}月{2}日 {3}时{4}分{5}秒") {
  // 获取 年月日 等信息
  let timeAry = this.match(/\d+/g);
  // template = 
  return template.replace(/\{(\d+)\}/g, (...[, $1]) => {
    // 这里就是用 $1为索引 拿到时间
    // let time = 
    return (timeAry[$1] || "0").padStart(2, "0");
    // return time;
  })
  // console.log(timeAry)
  // return template;
}
// 添加到字符串原型 (在axios中有所使用的方式)
["formatTime"].forEach(item => {
  String.prototype[item] = eval(item);
})
let time = "2022-03-07 12:12:20";
// 转为 指定格式的时间
console.log(time.formatTime())

queryURLParams解析

// 转为 指定格式的时间
/**
 * 
 * @param {*} template 模板 期望的日期格式
 *    模板规则: {0} 年 ...{1-5}月日时分秒
 * @returns 格式化好的时间字符串
 */
function formatTime(template = "{0}年{1}月{2}日 {3}时{4}分{5}秒") {
  // 获取 年月日 等信息
  let timeAry = this.match(/\d+/g);
  return template.replace(/\{(\d+)\}/g, (...[, $1]) => {
    return (timeAry[$1] || "0").padStart(2, "0");
  })
}
// 获取URL地址问号和上面的参数信息(也可能包含HASH值)
/**
 * @return 把所有问号后参数信息以键值对的方式存储起来返回
 */
function queryURLParams() {
  let obj = {};
  this.replace(/([^?=&#]+)=([^?=&#]+)/g, (...[, $1, $2]) => obj[$1] = $2);
  this.replace(/#(([^?=&#]+))/g, (...[, $1]) => obj["HASH"] = $1);
  return obj;
}
// 添加到字符串原型 (在axios中有所使用的方式)
["formatTime", "queryURLParams"].forEach(item => {
  String.prototype[item] = eval(item);
});

let url = "http://www.baidu.com?name=mao&age=22#abc"
console.log(url.queryURLParams())

正则表达式之千分符

// 转为 指定格式的时间
/**
 * 
 * @param {*} template 模板 期望的日期格式
 *    模板规则: {0} 年 ...{1-5}月日时分秒
 * @returns 格式化好的时间字符串
 */
function formatTime(template = "{0}年{1}月{2}日 {3}时{4}分{5}秒") {
  // 获取 年月日 等信息
  let timeAry = this.match(/\d+/g);
  return template.replace(/\{(\d+)\}/g, (...[, $1]) => {
    return (timeAry[$1] || "0").padStart(2, "0");
  })
}
// 获取URL地址问号和上面的参数信息(也可能包含HASH值)
/**
 * @return 把所有问号后参数信息以键值对的方式存储起来返回
 */
function queryURLParams() {
  let obj = {};
  this.replace(/([^?=&#]+)=([^?=&#]+)/g, (...[, $1, $2]) => obj[$1] = $2);
  this.replace(/#(([^?=&#]+))/g, (...[, $1]) => obj["HASH"] = $1);
  return obj;
}
/**
 * 正则表达式之千分符 也就是大数字 转为 三个数字一个逗号分隔的形式
 */
function millimeter() {
  // 需要正向预查 12, 345 678
  return this.replace(/\d{1,3}(?=(\d{3})+$)/g, content => content + ",");
}

// 添加到字符串原型 (在axios中有所使用的方式)
["formatTime", "queryURLParams", "millimeter"].forEach(item => {
  String.prototype[item] = eval(item);
});
let num = "123456789"; // 123,456,789
console.log(num.millimeter())
console.log("12345678".millimeter())

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尤雨东

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值