# 正则表达式

正则表达式是一个对象

正则表达式并不是 JS 独有的技术,基本上所有语言都支持这个技术

正则表达式的目的也很单纯,核心作用就是验证数据的合法性

正则表达式的特点:

1、正则表达式在 JS 中只针对字符串起作用

2、正则表达式会根据你设置的规则,对字符串进行 提取,搜索、替换 等操作

3、JavaScript 中正则表达式是一个内置对象,这个对象通过 RegExp () 创建,也可以直接通过赋值一个正则表达式来创建

第一种:

var reg = new RegExp(正则表达式)

第二种:

var reg = //

方法:

1、test () 用于验证某一个字符串是否符合正则的规则

var str ="abc";
var reg = /a/;
reg.test(str);

2、exec()根据正则表达式提取字符串中符合要求的字符,用法与上面类似,注意:正则表达式是可以添加修饰符的,这个修饰符会影响提取字符串的结果

# 修饰符

1、g 代表 global 全局验证

2、i 代表忽略大小写

3、m 代表多行的意思,也就是可以换行匹配

# 正则表达式的规则

# 一元符号

. 匹配除换行符以外的任意符号

\w 匹配字符数字和下划线

\S 匹配任何空白符

\d 匹配所有数字,与 [0-9]

\b 匹配单词边界

| 或匹配

^ 匹配字符串的开始

$ 匹配字符串的结束

# 原子组和原子表

# 原子表 [ ]

var reg = /^[张王赵]三$/
console.log(reg.test("张三"));                        //true
console.log(reg.test("王三"));                        //true
console.log(reg.test("赵三"));                        //true
console.log(reg.test("李三"));                        //false
console.log(reg.test("张四"));                        //false
console.log(reg.test("张三ssss"));                    //false
console.log(reg.test("张ssss三"));                    //false

原子表是用中括号的形式存在,它会从这个表中拿出一个进行条件匹配

在原子表中,还可以写范围

var reg1 = /[0-9]/
var reg2 = /[a-z]/
var reg3 = /[A-Z]/

注意:原子表中的范围不能倒写,会报错

# 原子组 ( )

var reg = /张三|王三|赵三/;
var reg2 = /(张|王|赵)三/    // 原子组用小括号表示

# 反义字符

** 在 [ ] 中使用

[^x] 匹配处理 x 以外的所有字符,这里的 x 可以是任意字符

[^xyz] 同上,匹配除了 xyz 以外的字符

\W 匹配除字母、数字、下划线以外的所有字符,等同 [^\w]

\B 匹配不是单词边界的字符

# 转义字符

\xnn 匹配十六进制数

\f 匹配换页符

\n 匹配换行符

\r 匹配回车符

\unnnn 匹配 unicode 编码

# 重复匹配

* 重复匹配零次或多次

+ 重复匹配 1 次或多次

? 重复匹配 0 次或 1 次

{n} 重复匹配 n 次

{n,} 至少重复匹配 n 次

{m,n} 重复普配 m 到 n 次 m < n

// 我希望验证一个字符串,它是 a 开始,c 结束,中间有 4 个英文字母。怎么写?
var reg = /^a[a-zA-Z]{4}c$/
// 我希望验证一个字符串,它是 a 开始,c 结束,中间有 4-6 个数字。怎么写?
var reg1 = /^a\d{4,6}c$/
// 我希望验证一个字符串,它是 a 开始,c 结束,中间至少有 4 个数字。怎么写?
var reg2 = /^a\d{4,}c$/
// 我希望验证一个字符串,它是 a 开始,c 结束,中间至少有 1 个或多个数字。怎么写?
var reg3 = /^a\d+c$/
// 我希望验证一个字符串,它是 a 开始,c 结束,中间至少有 0 个或多个数字。怎么写?
var reg4 = /^a\d*c$/
// 我希望验证一个字符串,它以三结束,以张开头,中间有 0 或 1 个字符
var reg5 = /^张.?三$/

# 贪婪与惰性

重复匹配默认都是按照贪婪的特性去匹配较多的次数,如果我们需要按照惰性特征来匹配

我们可以把上面的多有的重复匹配符号后面加上一个 ?表示惰性匹配

var str = "cawqdewfeffwerderwe";
// 我要提取 c 开始到 d 结束中间任意长度的字符串
var reg = /c[a-z]*d/;
var newStr = reg.exec(str);
// 这个时候我们的正则表达式处在贪婪模式下,它会执行贪婪(0 到贪婪多次)
var reg = /c[a-z]*?d/;  // 执行惰性重复匹配

# 原子组编号:\n

原子组通过()形成一个分组,这个分组会给一个分组编号

var str = "<div></div>";
// 现在希望通过正则验证这个标签
var reg = /<[a-zA-Z]+><\/[a-zA-Z]+>/;
// 上面的问题在于,我们希望开始标签和结束标签里面匹配到的是相同的字符
var flag = reg.test(str);

在上面的表达式中,我们表面上看起来是完成了功能,但是又有隐患,如果我们要匹配的字符串是 <div></span> 这样也会验证成功,但是不符合要求

我们的主要需求是开始标签和结束标签要一致,我们开始匹配的内容 <[a-zA-Z]+> 要与后面匹配的 <\/[a-zA-Z]+> 保持一致,这个时候我们就要用到原子组

var str = "<div></span>";
// 现在希望通过正则验证这个标签
var reg1 = /<[a-zA-Z]+><\/[a-zA-Z]+>/;
// 上面的问题在于,我们希望开始标签和结束标签里面匹配到的是相同的字符
var flag1 = reg1.test(str);
var reg2 = /<([a-zA-Z]+)><\/\1>/;
var flag2 = reg2.test(str);

# 前瞻后顾 :()语法

首先我们要弄清除两个条件

1、你的匹配条件是什么

2、你的限制条件是什么

# 前瞻

匹配条件是 A,限制条件是 B,B 要出现在 A 的后面

A(?=B)

如果要匹配 abc 并且 abc 要在 123 的前面

/abc(?=123)/g // 全局匹配 abc 后面紧跟 123
//abc123  true
//abc456  false
//123abc  false

# 负前瞻

匹配条件是 A,限制条件是 B,B 不能出现在 A 的后面

/abc(?!123)/g

# 后顾

匹配条件是 A,限制条件是 B,B 要出现在 A 的前面

(?<=B)A

要匹配 abc 并且 abc 要在 123 的后面

/(?<=123)abc/g
//abc123  false
//abc456  false
//123abc  true

# 负后顾

匹配条件是 A,限制条件是 B,B 要不能出现在 A 的前面

(?<!B)A

# 特殊情况

如何匹配中文

var reg = /^[\u4E00-\u9FA5]{2,4}$/

# 正则表达式(github)

regular expression : RegExp

用来处理字符串的规则

  • 只能处理字符串
  • 它是一个规则:可以验证字符串是否符合某个规则 (test) ,也可以把字符串中符合规则的内容捕获到 (exec /match ...)
let str = "good good study, day day up! ";
//=> 学正则就是用来制定规则 (是否包含数字)
let reg = /\d+/;
reg.test(str); //=> false
str = "2022-08-05";
reg.exec(str); //=>["2019",index:0,inputs:"原始字符串"]

# 编写正则表达式

# 创建方式有两种

//=> 字面量创建方式 (两个斜杆之间包起来的,都是用来描述规则的元字符)
let reg1 = /\d+/;
//=> 构造函数模式创建  两个参数:元字符字符串,修饰符字符串
let reg2 = new RegExp("\\d+");

正则表达式由两部分组成

  • 元字符
  • 修饰符
/* 常用的元字符 */
//=>1. 量词元字符:设置出现的次数
*  零到多次
+  一到多次
?  零次或者一次
{n}  出现n次 (n需为0或者正整数)
{n,}  出现n到多次
{n,m}  出现n到m次 (包含n和m)
//=>2. 特殊元字符:单个或者组合在一起代表特殊的含义
\    转义字符(普通->特殊->普通)
.    除\n (换行符) 以外的任意字符
^    以哪一个元字符作为开始
$    以哪一个元字符作为结束
\n   换行符
\d   0~9之间的数字
\D0~9之间的数字 (大写和小写的意思是相反的)
\w   数字、字母、下划线中的任意一个字符
\s   一个空白字符 (包含空格、制表符、换页符等)
\t   一个制表符 (一个TAb键:四个空格)
\b   匹配一个单词的边界
x|y  x或者y中的一个字符  (1|5|9)
[xyz] x或者y或者z中的一个字符  [adghve2375]
[^xy] 除了x/y以外的任意字符
[a-z] 指定a-z这个范围中的任意字符  [0-9a-zA-Z_](0-9或者a-z或者A-Z或者_中的一个字符) === \w
[^a-z] 上一个取反"非"
()    正则中的分组符号
(?:)  只匹配不捕获
(?=)  正向预查
(?!)  负向预查
//=>3. 普通元字符:代表本身含义
/jiangbing/ 此正则匹配的就是"jiangbing"
/* 正则表达式常用的修饰符: img*/
i =>ignoreCase  忽略单词大小写匹配
m =>multiline   可以进行多行匹配
g =>global      全局匹配
/*
 *	/A/.test('lalala'); //=>fasle
 *	/A/i.test('lalala'); //=>true
 */

# 元字符详细解析

^$

let reg = /^\d/;
console.log(reg.test("jiangbing")); //=>fasle
console.log(reg.test("2022jiangbing")); //=>true
console.log(reg.test("jiangbing2022")); //=>false
let reg = /\d$/;
console.log(reg.test("jiangbing")); //=>fasle
console.log(reg.test("2022jiangbing")); //=>false
console.log(reg.test("jiangbing2022")); //=>true
//=>^/$ 两个都不加:字符串中包含符合规则的内容即可
let reg = /\d+/;
//=^/$ 两个都加:字符串只能是和规则一致的内容
let reg = /^\d+$/;
//=> 举个例子:验证手机号码 (11 位,第一个数字是 1 即可)
let reg = /^1\d{10}$/;

\

//=>. 不是小数点,是除 \n 外的任意字符
let reg = /^2.3$/;
console.log(reg.test("2.3")); //=>true
console.log(reg.test("2@3")); //=>true
console.log(reg.test("23")); //=>fasle
//=> 基于转义字符,让其只能代表小数点
let reg = /^2\.3$/;
console.log(reg.test("2.3")); //=>true
console.log(reg.test("2@3")); //=>false
let str = "\\d";
reg = /^\d$/;  //=>\d 代表 0-9 的数字
console.log(reg.test("str")); //=>fasle
reg = /^\\d$/;  //=> 把特殊符号转换为普通的
console.log(reg.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
console.log(reg.test("82")); //=>fasle
//--- 直接 x|y 会存在很乱的优先级问题,一般我们写的时候都伴随着小括号进行分组,因为小括号改变处理的优先级 => 小括号:分组
let reg = /^(18|29)$/;
console.log(reg.test("18")); //=>true
console.log(reg.test("29")); //=>true
console.log(reg.test("129")); //=>fasle
console.log(reg.test("189")); //=>fasle
//=> 只能是 18 或者 29 中的一个了

[]

//1. 中括号中出现的字符一般都代表本身的含义
let reg = /^[@+]$/;
console.log(reg.test("@")); //=>true
console.log(reg.test("+")); //=>true
console.log(reg.test("@@")); //=>false
console.log(reg.test("@+")); //=>false
reg = /^[\d]$/; //=>\d 在中括号中还是 0-9
console.log(reg.test("d")); //=>false
console.log(reg.test("\\")); //=>false
console.log(reg.test("9")); //=>true
reg = /^[\\d]$/; //=>\d 已经被 \ 转义
console.log(reg.test("d")); //=>true
console.log(reg.test("\\")); //=>true
console.log(reg.test("9")); //=>false
//2. 中括号中不存在多位数
reg = /^[18]$/;
console.log(reg.test("1")); //=>true
console.log(reg.test("8")); //=>true
console.log(reg.test("18")); //=>false
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. 验证是否为有效数字

/*
 *	规则分析
 *		1. 可能出现 + - 号,也可能不出现  [+-]?
 *		2. 一位 0-9 都可以,多位首位不能为 0  (\d|([1-9]\d+)
 *		3. 小数点部分可能有可能没有,一旦有后面必须有小数点 + 数字  (\.\d+)?
 */
let reg = /^[+-]?(\d|([1-9]\d+))(\.\d+)?$/;

2. 验证密码

//=> 数字、字母、下划线
//=>6~16 位
let val = userPassInp.value,
    reg = /^\w{6,16}$/;
let flag = reg.test(val);
/*
function checkPass (val){
    if (val.length <6 || val.length >16){
        alert (' 长度必须介于 6-16 位之间!');
        retrun;
    }
    let area = ['a','b'...'_']; //=> 包含数字、字母、下划线
    for (let i=0;i<val.length;i++){
        let char = val [i];
        if (!area.includes (char)){
            alert ("格式不正确");
            return;
        }
    }
}
*/

3. 验证真实姓名

/*
 *	1. 验证汉字  /^[\u4E00-\u9FA5]$/
 *	2. 名字长度 2~10 位
 *	3. 可能有译名・汉字  (・[\u4E00-\u9FA5]{2,10}){0,2}
 */
let reg = /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{2,10}){0,2}$/;

4. 验证邮箱

let reg = /^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/;
//=> \w+((-\w+)|(\.\w+))*
// 1. 开头是数字字母下划线 (1 到多位)
// 2. 还可以是 "- 数字字母下划线 或者。数字字母下划线",整体零到多次
// 邮箱的名字由 "数字、字母、下划线、-、." 几部分组成,但是 -/. 不能连续出现也不能作为开始
//=> @[A-Za-z0-9]+
// 1.@后面紧跟着:数字、字母 (1 - 多位)
//=> ((\.|-)[A-Za-z0-9]+)*
// 1. 对 @后面名字的补充
// 多域名   .com.cn
// 企业域名  zxt@zhufeng-peixun-office.com
//=> \.[A-Za-z0-9]+
// 1. @xxx.com/ @xxx.cn 这个匹配的是最后的域名 (.com/.cn/.org/.edu/.net/.vip...)

5. 身份证号码

/*
 *	1. 一共 18 位
 *	2. 最后一位可能是 X
 *
 *	身份证前六位:省市县 130828
 *	中间八位:年月日
 *	最后四位:
 *		最后一位 => X 或者数字
 *		倒数第二位 => 偶数 女  奇数 男
 *		其余的是经过算法算出来的
 */
//let reg = /^\d{17}(\d|X)$/;
//=> 小括号分组的第二个作用:分组捕获,不仅可以把大正则匹配的信息捕获到,还可以单独捕获到每个小分组的内容
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(\d|X)$/;
reg.exec("130828199012040617");//=>['130828199012040617', '130828', '1990', '12', '04', '1', '7', index: 0, input: '130828199012040617', groups: undefined] 捕获结果是一个数组,包含每一个小分组单独获取的内容

正则两种创建方式的区别

//=> 构造函数因为传递的是字符串,\ 需要写两个才代表斜杆
let reg = /\d+/g;
reg = new RegExp("\\d+","g");
//=> 正则表达式中的部分内容是变量存储的值
// 1. 两个斜杆中间包起来的都是元字符 (如果正则中要包含某个变量的值,则不能使用字面量方式创建)
let type = 'zhufeng';
reg = /^@"+type+"@$/;
console.log(reg.test("@zhufeng@")); //=>false
console.log(reg.test('@"""typeeee"@')); //=true
// 2. 这种情况只能使用构造函数方式 (因为它传递的规则是字符串,只有这样才能进行字符拼接)
reg = new RegExp("^@"+type+"@$");
console.log(reg.test("@zhufeng@")); //=>true

# 正则的捕获

实现正则捕获的办法

  • 正则 RegExp.prototype 上的方法
    • exec
    • test
  • 字符串 String.prototype 上支持正则表达式处理的方法
    • replace
    • match
    • splite
    • ......
let str = "zhufeng2022yangfan2023qihang2024";
let reg = /\d+/;
/*
 *	基于 exec 实现正则的捕获
 *		1. 捕获到的结果是 null 或者一个数组
 *			数组第一项:本次捕获到的内容
 *			其余项:对应小分组本次单独捕获的内容
 *			index: 当前捕获内容在字符串中的起始索引
 *			input: 原始字符串
 *		2. 每执行一次 exec,只能捕获到一个符合正则规则的,但是默认情况下,我们执行 100 遍,获取的结果永远都是第一个匹配到的,其余的捕获不到
 *		=>"正则捕获的懒惰性": 默认只捕获第一个
 */
console.log(reg.exec(str)); //=>['2022', index: 7, input: 'zhufeng2022yangfan2023qihang2023']
/*
//=> 实现正则捕获的前提是:当前正则要和字符串匹配,如果不匹配捕获的结果是 null
let reg = /^\d+$/;
console.log (reg.test (str)); //=>fasle
console.log (reg.exec (str)); //=>null

# 懒做性的解决办法

let str = "zhufeng2022yangfan2023qihang2024";
let reg = /\d+/;
/* 
 *  reg.lastIndex: 当前正则下一次匹配的起始索引位置
 *  懒惰性捕获的原因:默认情况下 lastIndex 的值不会被修改,每一次都是从字符串开始位置查找,所以找到的永远只是第一个
 *  解决办法:全局修饰符 g
 */
/* console.log (reg.lastIndex); //=>0 下面匹配捕获是从 str 索引零的位置开始找
   console.log (reg.exec (str));
   console.log (reg.lastIndex); //=>0 第一次匹配捕获完成,lastIndex 没有改变,所以下一次 exec 依然是从字符串最开始找,找到的永远是第一个匹配到的 
 */
/* reg = /\d+/g;
   console.log (reg.exec (str)); //=>["2022"...]
   console.log (reg.lastIndex); //=>11 设置全局匹配修饰符 g 后,第一次匹配完,lastIndex 会自己修改
   console.log (reg.exec (str)); //=>["2023"...]
   console.log (reg.lastIndex); //=>22
   console.log (reg.exec (str)); //=>["2024"...]
   console.log (reg.lastIndex); //=>32
   console.log (reg.exec (str)); //=>null  当全部捕获后,再次捕获的结果是 null,但是 lastIndex 又回归了初始值零,再次捕获又从第一个开始了...
   console.log (reg.lastIndex); //=>0
   console.log (reg.exec (str)); //=>["2019"...] 
 */
/* reg = /\d+/g;
   if (reg.test (str)) {
   		//=> 验证一下:只有正则和字符串匹配我们再捕获
        console.log (reg.lastIndex); //=>11  基于 test 匹配验证后,lastIndex 已经被修改为第一次匹配后的结果,所以下一次捕获不再从头开始
        console.log (reg.exec (str)); //=>["2023"...]
    } 
 */
//=> 需求:编写一个 execAll,执行一次可以把所有匹配的结果捕获到 (前提正则一定要设置全局修饰符 g)
~ function() {
    function execAll(str = "") {
        //=>str: 需要匹配的字符串
        //=>this: RegExp 的实例 (当前操作的正则)
        //=> 进来后第一件事,是验证当前正则是否设置了 g,不设置则不能在进行循环捕获了,否则会导致死循环
        if (!this.global) return this.exec(str);
        //=>ary 存储最后所有捕获的信息   res 存储每一次捕获的内容
        let ary = [],
            res = this.exec(str);
        while (res) {
            //=> 把每一次捕获的内容 res [0] 存放到数组中
            ary.push(res[0]);
            //=> 只要捕获的内容不为 null,则继续捕获下去
            res = this.exec(str);
        }
        return ary.length === 0 ? null : ary;
    }
    RegExp.prototype.execAll = execAll;
}();
reg = /\d+/g;
console.log(reg.execAll(str));
//=> 字符串中的 match 方法,可以在执行一次的情况下,捕获到所有匹配的数据 (前提:正则也得设置 g 才可以)
console.log(str.match(reg));

# 正则的分组捕获

//=> 身份证号码
let str = "130828199012040112";
let reg = /^(\d{6})(\d{4})(\d{2})(\d{2})\d{2}(\d)(?:\d|X)$/;
console.log(reg.exec(str)); //=>['130828199012040112', '130828', '1990', '12', '04', '1', index: 0, input: '130828199012040112']
console.log(str.match(reg)); //=>['130828199012040112', '130828', '1990', '12', '04', '1', index: 0, input: '130828199012040112']
//=> 第一项:大正则匹配的结果
//=> 其余项:每一个小分组单独匹配捕获的结果
//=> 如果设置了分组 (改变优先级),但是捕获的时候不需要单独捕获,可以基于?: 来处理
//=> 既要捕获到 {数字},也想单独的把数字也获取到,例如:第一次找到 {0} 还需单独获取 0
let str = "{0}年{1}月{2}日";
/*
	//=> 不设置 g 只匹配一次,exec 和 match 获取的结果一致 (既有大正则匹配的信息,也有小分组匹配的信息)
	let reg = /\{(\d+)\}/;
	console.log (reg.exec (str)); //=>['{0}','0',...]
	console.log (str.match (reg)); //=>['{0}','0',...]
*/
let reg = /\{(\d+)\}/g;
//console.log (str.match (reg)); //=> ['{0}', '{1}', '{2}']	 多次匹配的情况下,match 只能把大正则匹配的内容获取到,小分组匹配的信息无法获取
let aryBig = [],
    arySmall = [],
    res = reg.exec(str);
while(res){
    let [big,small] = res;
    aryBig.push(big);
    arySmall.push(small);
    res = reg.exec(str);
}
console.log(aryBig,arySmall); //=>['{0}', '{1}', '{2}'] (3) ['0', '1', '2']
//=> 分组的第三个作用: "分组引用"
let str = "book"; //=>"good"、"look"、"moon"、"foot"...
let reg = /^[a-zA-Z]([a-zA-Z])\1[a-zA-Z]$/; //=> 分组引用就是通过 "\ 数字" 让其代表和对应分组出现一模一样的内容
console.log(reg.test("book")); //=>true
console.log(reg.test("deep")); //=>true
console.log(reg.test("some")); //=>false

# 正则捕获的贪婪性

let str = "珠峰2022@2023培训";
//=> 正则捕获的贪婪性:默认情况下,正则捕获的时候,是按照当前正则所匹配的最长结果来获取的
let reg = /\d+/g;
console.log(str.match(reg)); //=>["2019","2022"]
//=> 在量词元字符后面设置?: 取消捕获时候的贪婪性 (按照正则匹配的最短结果来获取) 
reg = /\d+?/g;
console.log(str.match(reg)); //=>['2', '0', '2', '2', '2', '0', '2', '3']

? 问号在正则中的五大作用

  • 问号左边是非量词元字符:本身代表量词元字符,出现零到一次
  • 问号左边是量词元字符:取消捕获时候的贪婪性
  • (?😃 只匹配不捕获
  • (?=) 正向预查
  • (?!) 负向预查

# 其他正则捕获的方法

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)); //=>fasle
console.log(RegExp.$1); //=>"2"	存储的是上次捕获的结果
//=>RegExp.$1~RegExp.$9: 获取当前本次正则匹配后,第一个到第九个分组的信息

2.replace 字符串中实现替换的方法 (一般都是伴随正则一起使用的)

let str = "zhufeng@2022|zhufeng@2023";
//=> 把 "zhufeng" 替换成 "珠峰"
// 1. 不用正则,执行一次只能替换一个
str = str.replace("zhufeng","珠峰").replace("zhufeng","珠峰");
console.log(str); //=>"珠峰 @2022 | 珠峰 @2023"
// 2. 使用正则会简单点
str = str.replace(/zhufeng/g,"珠峰");
console.log(str); //=>"珠峰 @2022 | 珠峰 @2023"
let str = "zhufeng@2022|zhufeng@2023";
//=> 把 "zhufeng" 替换成 "zhufengpeixun"
str = str.replace("zhufeng","zhufengpeixun").replace("zhufeng","zhufengpeixun");
console.log(str); //=>"zhufengpeixunpeixun@2022|zhufeng@2023" 每一次替换都是从字符串第一个位置开始找的 (类似于正则捕获的懒惰性)
//=> 基于正则 g 可以实现
str = str.replace(/zhufeng/g,"zhufengpeixun");
console.log(str); //=>"zhufengpeixun@2022|zhufengpeixun@2023"

案例:把时间字符串进行处理

let time = "2022-08-09";
//=> 变为 "2022 年 08 月 09 日"
let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
//=> 这样可以实现
//time = time.replace (reg,"$1 年 $2 月 $3 日");
//console.log (time); //=>"2022 年 08 月 09 日"
//=> 还可以这样处理 [str].replace ([reg],[function])
// 1. 首先拿 reg 和 time 进行匹配捕获,能匹配到几次就会把传递的函数执行几次 (而且是匹配一次就执行一次)
// 2. 不仅把方法执行,而且 replace 还给方法传递了实参信息 (和 exec 捕获的内容一致的信息:大正则匹配的内容,小分组匹配的信息....)
// 3. 在函数中我们返回的是啥,就把当前大正则匹配的内容替换成啥
/*
	time = time.replace (reg,(big,$1,$2,$3)=>{
    	//=> 这里的 $1~$3 是我们自己设置的变量
    	console.log (big,$1,$2,$3); //=>"2022-08-09 2022 08 09"
	});
 */
time = time.replace(reg,(...arg)=>{ 
    console.log(arg);
    let  [,$1,$2,$3] = arg;
    $2.length<2 ? $2="0"+$2 : null;
    $3.length<2 ? $3="0"+$3 : null;
    return $1+"年"+$2+"月"+$3+"日";
});

单词首字母大写

let str = "good good study, day day up! ";
let reg = /\b([a-zA-Z])[a-zA-Z]*\b/g;
//=> 函数被执行了六次,每一次都把正则匹配信息传递给函数
//=> 每一次 arg: ['good','g'] ['good','g'] ['good','g'] ['study','s']...
str = str.replace(reg,(...arg)=>{
    let [content,$1] = arg;
    $1 = $1.toUpperCase();
    content = content.substring(1);
    return $1 + content;
});
console.log(str); //=>"Good Good Study, Day Day Up! "

验证一个字符串中哪个字母出现的次数最多,多少次?

let str = "zhufengpeixunzhoulaoshi";
/*
 *	去重思维
 */
let obj = {};
[].forEach.call(str,char=>{
    if(typeof obj[char] !== "undefined"){
        obj[char++];
        return;
    }
    obj[char] = 1; 
});
console.log(obj);let obj = {};
[].forEach.call(str, char => {
    if (typeof obj[char] !== "undefined") {
        obj[char]++;
        return;
    }
    obj[char] = 1;
});
let max = 1,
    res = [];
for (let key in obj) {
    let item = obj[key];
    item > max ? max = item : null;
}
for (let key in obj) {
    let item = obj[key];
    if (item === max) {
        res.push(key);
    }
}
console.log(`出现次数最多的字符是:${res},出现了${max}`); //=> 出现次数最多的字符是:h,u,出现了 3 次
/*
 *	排序
 */
str = str.split('').sort((a, b) => a.localeCompare(b)).join('');
//console.log(str); //=>"aeefghhhiilnnoopsuuuxzz"
let reg = /([a-zA-Z])\1+/g;
let ary = str.match(reg); //=>['ee', 'hhh', 'ii', 'nn', 'oo', 'uuu', 'zz']
ary.sort((a, b) => b.length - a.length);
console.log(ary); //=>['hhh', 'uuu', 'ee', 'ii', 'nn', 'oo', 'zz']
let max = ary[0].length,
    res = [ary[0].substr(0, 1)];
for (let i = 1; i < ary.length; i++) {
    let item = ary[i];
    if (item.length < max) {
        break;
    }
    res.push(item.substr(0, 1));
}
console.log(`出现次数最多的字符是:${res},出现了${max}`); //=> 出现次数最多的字符是:h,u,出现了 3 次
/*
 *	正则
 */
let max = 0,
    res = [],
    flag = false;
str = str.split('').sort((a, b) => a.localeCompare(b)).join('');
// console.log(str); //=>"aeefghhhiilnnoopsuuuxzz"
for (let i = str.length; i > 0; i--) {
    let reg = new RegExp("([a-zA-Z])\\1{" + (i - 1) + "}", "g");
    // i=3  /([a-zA-Z])\1{2}/  "hhh" "uuu"
    str.replace(reg, (content, $1) => {
        res.push($1);
        max = i;
        flag = true;
    });
    if (flag) break;
}
console.log(`出现次数最多的字符是:${res},出现了${max}`); //=> 出现次数最多的字符是:h,u,出现了 3 次

时间字符串格式化

~ function() {
    /* 
     *  formatTime: 时间字符串的格式化处理
     */
    /* function formatTime () {
    	// 1. 首先获取时间字符串中的年月日等信息
        let timeAry = this.match (/\d+/g);
        //console.log (timeAry)//['2022', '8', '10', '16', '00', '43']
        let template = "{0} 年 {1} 月 {2} 日 {3} 时 {4} 分 {5} 秒";
        template = template.replace (/\{(\d+)\}/g, (content, $1) => {
        	//=>content: 当前本次大正则匹配的信息   $1: 本次小分组单独匹配的信息
        	// 以 $1 的值作为索引,到 timeAry 中找到对应的时间 (如果没有则用 "00" 补)
        	let time = timeAry [$1] || "00";
        	time.length < 2 ? time = "0" + time : null;
            return time;
        })
        return template;
    }*/
    //=> 简化版本
    /* 
     *  formatTime: 时间字符串的格式化处理
     *  @params
     *      template: [string] 我们最后期望获取日期格式的模板
     *      模板规则: {0}-> 年  {1~5}-> 月日时分秒
     *  @return
     *      [string] 格式化后的时间字符串
     *  by 江冰 on 2022-8-10 16:33:43
     */
    function formatTime(template = "{0}年{1}月{2}日 {3}时{4}分{5}秒") {
        let timeAry = this.match(/\d+/g);
        return template = template.replace(/\{(\d+)\}/g, (...[, $1]) => {
            let time = timeAry[$1] || "00";
            return time.length < 2 ? time = "0" + time : time;
        });
    }
    /* 扩展到内置类 String.prototype 上 */
    ["formatTime"].forEach(item => {
        String.prototype[item] = eval(item);
    });
}();
//=> 服务器获取的
// "2022-8-10 16:00:43"
// "2022/8/10 16:00:43"
// "2022/8/10"
//=> 想要转变为的格式
// "08 月 10 日 16 时 00 分"
// "2022 年 8 月 10 日"
// ...
let time = "2022-8-10 16:00:43";
console.log(time.formatTime()); //=>2022 年 08 月 10 日 16 时 00 分 43 秒
console.log(time.formatTime("{0}年{1}月{2}日")); //=>2022 年 08 月 10 日
console.log(time.formatTime("{1}-{2} {3}:{4}")); //=>08-10 16:00
time = "2022/8/13";
console.log(time.formatTime()); //=>2022 年 08 月 13 日 00 时 00 分 00 秒
console.log(time.formatTime("{0}年{1}月{2}日")); //=>2022 年 08 月 13 日
console.log(time.formatTime("{1}-{2} {3}:{4}")); //=>08-13 00:00

queryURLParams

~ function() {
    /* 
     *  queryURLParams: 获取 URl 地址问号和后面的参数信息 (可能也包含 HASH 值)
     *  @params   
     *  @return
     *      [object] 把所有问号参数信息以键值对的方式存储起来并且返回
     *  by 江冰 on 2022-8-10 16:33:43
     */
    function queryURLParams() {
        let obj = {};
        this.replace(/([^?=&#]+)=([^?=&#]+)/g, (...[, $1, $2]) => obj[$1] = $2);
        this.replace(/#([^?=&#]+)/g, (...[, $1]) => obj['HASH'] = $1);
        return obj;
    }
    
    /* 扩展到内置类 String.prototype 上 */
    ["queryURLParams"].forEach(item => {
        String.prototype[item] = eval(item);
    });
}();
let url = "http://www.zhufengpeixun.cn/?lx=1&from=wx#video";
console.log(url.queryURLParams()); //=>{lx: '1', from: 'wx', HASH: 'video'}

千分符

~ function() {
	/* 
     *  millimeter: 实现大数字的千分符处理
     *  @params    
     *  @return
     *      [string] 千分符后的字符串
     *  by 江冰 on 2022-8-10 16:33:43
     */
	function millimeter() {
		return this.replace(/\d{1,3}(?=(\d{3})+$)/g, content => content + ',');
    }
    /* 扩展到内置类 String.prototype 上 */
    ["millimeter"].forEach(item => {
        String.prototype[item] = eval(item);
    });
}();
let num = "15628954"; //=>"15,628,954" 千分符
console.log(num.millimeter()); //=>"15,628,954"
// 把字符串倒过来
num = num.split('').reverse().join('');
for (let i = 2; i < num.length - 1; i += 4) {
    let prev = num.substring(0, i + 1),
        next = num.substring(i + 1);
    num = prev + "," + next;
}
num = num.split('').reverse().join('');
console.log(num); //=>"15,628,954"

# 常用的正则表达式

// 正整数
/^[0-9]*[1-9][0-9]*$/;
// 负整数
/^-[0-9]*[1-9][0-9]*$/;
// 正浮点数
/^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$/;
// 负浮点数
/^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$/;
// 浮点数
/^(-?\d+)(\.\d+)?$/;
//email 地址
/^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/;
//url 地址
/^[a-zA-z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\S*)?$/;
或:^http:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&_~`@[\]\':+!]*([^<>\"\"])*$
// 年 / 月 / 日(年 - 月 - 日、年。月。日)
/^(19|20)\d\d[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$/;
// 匹配中文字符
/[\u4e00-\u9fa5]/;
// 匹配帐号是否合法 (字母开头,允许 5-10 字节,允许字母数字下划线)
/^[a-zA-Z][a-zA-Z0-9_]{4,9}$/;
// 匹配空白行的正则表达式
/\n\s*\r/;
// 匹配中国邮政编码
/[1-9]\d{5}(?!\d)/;
// 匹配身份证
/\d{15}|\d{18}/;
// 匹配国内电话号码
/(\d{3}-|\d{4}-)?(\d{8}|\d{7})?/;
// 匹配 IP 地址
/((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)/;
// 匹配首尾空白字符的正则表达式
/^\s*|\s*$/;
// 匹配 HTML 标记的正则表达式
< (\S*?)[^>]*>.*?|< .*? />;
//sql 语句
^(select|drop|delete|create|update|insert).*$
// 提取信息中的网络链接
(h|H)(r|R)(e|E)(f|F) *= *('|")?(\w|\\|\/|\.)+('|"| *|>)?
// 提取信息中的邮件地址
\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*
// 提取信息中的图片链接
(s|S)(r|R)(c|C) *= *('|")?(\w|\\|\/|\.)+('|"| *|>)?
// 提取信息中的 IP 地址
(\d+)\.(\d+)\.(\d+)\.(\d+)
// 取信息中的中国手机号码
(86)*0*13\d{9}
// 提取信息中的中国邮政编码
[1-9]{1}(\d+){5}
// 提取信息中的浮点数(即小数)
(-?\d*)\.?\d+
// 提取信息中的任何数字
(-?\d*)(\.\d+)?
// 电话区号
^0\d{2,3}$
// 腾讯 QQ 号
^[1-9]*[1-9][0-9]*$
// 帐号(字母开头,允许 5-16 字节,允许字母数字下划线)
^[a-zA-Z][a-zA-Z0-9_]{4,15}$
// 中文、英文、数字及下划线
^[\u4e00-\u9fa5_a-zA-Z0-9]+$
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

dmq 微信支付

微信支付

dmq 支付宝

支付宝