0.简介
regular expression,规则表达式,是一种用来处理字符串的规则,巧妙的正则表达式可以节省很多判断代码.
1.组成
- 元字符
- 特殊元字符
\ 转义字符 => 普通字符 <=> 特殊字符
. 点 => 代表除了\n 以外的任意字符(字符字符字符)
^ => 以哪个元字符开始
$ => 以哪个元字符作为结束
\n 任意一个换行符
\d 0-9之间的一个数字
\D 除了0-9之间的任意字符
\w 数字字母下划线中的任意一个字符
\W
\s 一个空白字符 空格 制表符 换页符等
\t 一个制表符 4个空格
\b 单词边界
x | y 或者
[xyz] => x | y | z
[^xy] => 除了 x | y 的任意字符
[a-z]
[^a-z]
(正则中的分组符号)
(?:) => 只匹配不捕获
(?=) => 正向预查
(?!) => 负向预查
- 量词元字符
* => 0-多次
+ => 1-多次
? => 0 - 1次
{n} => 出现n次
{n,m} => 出现n-m次
- 普通元字符
/abc/ 匹配abc
- 修饰符
- i ignoreCase
忽略单词大小写- m multiLine
进行多行匹配- g global
全局匹配
2.贪婪性
let str = 'ab2020nb',
// 贪婪性:默认情况下,捕获的时候,按照当前正则匹配的最长结果来获取
reg = /\d+?/g
console.log(str.match(reg))
// 在量词后面设置? 取消捕获时候的贪婪性 按照正则匹配的最短结果来获取
/*
* 问号的作用
* 问号左边是非量词元字符:则此时问号本身代表两次元字符出现0-1次
* 问号左边是量词元字符:取消捕获时候的贪婪性 /\d+?/g
* (?:) 只匹配不捕获
* (?=) 正向预查
* (!=) 负向预查
*/
3.懒惰型
/*
* 基于exec的捕获
* 捕获到的结果是一个数组或者null
* 第一项:本次捕获到的内容
* 其余项:对应小分组本次单独捕获的内容
* 当前捕获内容在字符串中的索引
* input:原始字符串
* 每执行一次exec只能捕获到一个符合正则规则的字符串,默认情况下执行多次也只能匹配第一次捕获的结果 => 正则捕获的懒惰型
* 懒惰产生的原因:每一次匹配的lastIndex值不变 只能通过全局修饰符才能改变这一特性
* lastIndex : 当前正则匹配下一次匹配的起始索引位置
*
* 设置全局匹配符g之后第一次匹配完成之后会自动修改lastIndex的值
* 此时可以进行循环多次进行匹配
*
*/
let str = 'nb13奥利给6666aaaa',
reg = /\d+/g
4.举例几个简单正则表达式
- 1).验证手机号
// \^abc0$\ 只能是abc0
// 验证手机号
let tel = /^1\d{10}$/ //1后面十位数字
let reg1 = /^2.3$/ //=> 只能匹配2中间一个除了换行之外的任意字符 3
console.log(reg1.test('2.3'))
console.log(reg1.test('253'))
console.log(reg1.test('2@3'))
let reg2 = /^2\.3$/ //=> 只能是2.3
console.log(reg2.test('2.3'))
let reg3 = /^\\d$/ // => 匹配 \d 特殊转换为普通
console.log(reg3.test('\\d'))
// 直接使用 /^18|69$/ 存在很乱的优先级问题
// /^(18|69)$/ 这样不存在优先级混乱问题
// [] 中括号出现的字符一般都是代表本身的含义 且不包含多位数 只能是一位数
// /^[@+]/$ 出现@ 或者+ 中的一个
// /^[\d]$/ \d在中括号中还是0-9数字
// /[10-29]/ 1 0 - 2 9
- 2).验证是否为有效数值
// 验证是否为有效数值
// 规则分析: 可能出现正负号 只出现一位 [+-]?
// 一位0-9 多位不能0开头 (\d | [1-9]\d+)
// 小数部分可能有可能没有 有小数点后面必须有数字 (\.\d+)?
// 重点重点重点 正则中不能随意添加空格,空格会被识别当做判别中的一位
let num = /^[+-]?(\d|([1-9]\d+))(\.\d+)$/
console.log(num.test('+0.1'))
// 更简单
isNaA(parseFloat(num)) === true
- 3)验证真实姓名
/*
*验证真实姓名
* 汉字范围 /^[\u4E00-\u9FA5]$/
* 名字长度2-10
* 可能有译名 .汉字
*
* /^[\u4E00-\u9FA5]{2,10}(·[\u4E00-\u9FA5]{2,10}){0,2}$/
*
*/
let reg = /(.)?/
reg.test('.')
- 验证邮箱
/*
*验证邮箱
* \w+((-\w+)|(\.\w+))*
* 开头是字母数字下划线(1-多位)
* 还可以输 -数字字母下划线 或者.数字字母下划线 *
* 邮箱的名字由数字字母下划线- . 几部分组成但是.-不能连续出现或者作为开头
*
* @[A-Za-z0-9]+
* @后面数字字母1-多位
*
* ((\.|-)[A-Za-z0-9]+)*
* 对@后面名字的补充
* 多域名 .com.cn
* 企业邮箱 zdz@jiazu-qiye.com
*
* \.[A-Za-z0-9]+
* 匹配最后的常规 .com 域名
*/
let email =
/^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-za-z0-9]+)*\.[A-Za-z0-9]+$/
- 身份证验证
/*
*身份证
*规则:
1.一共18位
2.最后一位可能是X 代表10
前六位:省市县
最后四位:
最后一位X或者数字
倒数第二位: 偶数女 奇数 男
其余两位经过算法算出
小括号还具有:分组捕获功能,不仅可以把大正则匹配的信息捕获,还可以单独捕获到每个小组的内容
*
*/
let id = /^(\d{6})(\d{4})(\d{2})(\d{2})(\d{2})(\d)(\d|X)$/
console.log(id.exec('37152219900101823X'))
5.以前学习的记录
let time = '2020-11-6' // => 2020年11月16日
let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})/g
time2 = time.replace(reg,'$1年$2月$3日')
console.log(time2)
// 思路:拿reg和time匹配捕获,匹配几次就会把传递的函数执行几次
// 不仅把方法执行而且replace还会把实参信息传递给函数
time1 = time.replace(reg,(...arg) => {
console.log(arg)
// 参数中第一项为捕获到的完整字符串,其后为每次捕获到的小分组
// 最后一项为匹配字符串 倒数第二项为第一次匹配的索引
let [,$1,$2,$3] = arg
function padZero(str) {
return ('00' + str).slice(str.length)
}
$2 = padZero($3)
$3 = padZero($2)
return $1 + '年' + $2 + '月' + $3
+ '日'
})
console.log(time1)
// 首字母大写 replace不改变原字符串
let reg1 = /\b([a-zA-Z])[a-zA-Z]+\b/g
let str1 = 'good good study day day up!'
// 函数执行6次
str1 = str1.replace(reg1,(...arg) => {
// 参数的第一项是匹配的完整分组 good 第二项是匹配到的小分组 g
console.log(arg)
let [content,$1] = arg
$1 = $1.toUpperCase()
content = content.slice(1)
return $1 + content
})
console.log(str1)
- 字母出现最大次数
/*
* 如何验证一个字符串中那个字符串出现的次数
*
*
*/
let str = 'aaabbbbbbbbccccbcccccd'
// 去重
function statastic(str) {
let obj = {},
max = 1,
res = []
;[].forEach.call(str, char => {
if (typeof obj[char] !== 'undefined') {
obj[char]++
return
}
obj[char] = 1
})
for (let key in obj) {
let item = obj[key]
item > max ? max = item : null
}
for (let key in obj) {
if (obj[key] === max) {
res.push(key)
}
}
// 此时res被转换为字符串
return `maxItem:${res}\nvalue:${max}`
}
console.log(statastic(str))
// 排序
function statastic1(str) {
// 将字符串转化为数组排序再转化为字符串
str = str.split('').sort((a, b) => a.localeCompare(b)).join('')
// 匹配重复字母
let reg = /([a-zA-Z])\1+/g
// 捕获
let arr = str.match(reg)
// 把捕获的结果数组根据长度排序 得到一个降序排列的数组里面包含结果数组字符串
// ["bbbbbbbb", "cccccccc", "aaa"]
arr.sort((a, b) => b.length - a.length)
console.log(arr)
let max = arr[0].length,
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))
}
return `maxItem:${res}\nvalue:${max}`
}
console.log(statastic1(str))
function statastic3(str) {
// 字母排序
str = str.split('').sort((a, b) => a.localeCompare(b)).join('')
let max = 0,
res = [],
flag = false;
console.log(str)
for (let i = str.length; i > 0; i--) {
// 查找出现次数最多的 从最大次数(字符串长度)开始检测
let reg = new RegExp('([a-zA-Z])\\1{' + (i - 1) + '}', 'g')
str.replace(reg, (content, $1) => {
res.push($1)
max = i
flag = true
// break //不能放在这里 只能放在循环中
})
if (flag) {
break
}
}
return `maxItem:${res}\nvalue:${max}`
}
console.log(statastic3(str))
- 时间字符串格式化
/*
* 服务器获取时间字符串格式
* 2020-11-17 09:00:00
* 2020/11/17 09:00:00
* 想要转化为的格式
* 2020年08月13日
* 08月13日 09时00分
*/
function formatTime1(template) {
// 1.获取字符串中年月日时间
let timeArr = this.match(/\d+/g)
template = template || "{0}年{1}月{2}日{3}时{4}分{5}秒"
template = template.replace(/\{(\d+)\}/g, (content, $1) => {
let time = timeArr[$1] || '00'
// 补0
function padZero(str) {
return ('00' + str).substr(str.length)
}
time = padZero(time)
return time
})
return template
}
// 简化代码
function formatTime(template = "{0}年{1}月{2}日{3}时{4}分{5}秒") {
let timeArr = this.match(/\d+/g)
// 直接解构赋值
return template.replace(/\{(\d+)\}/g, (...[,$1]) => {
let time = timeArr[$1] || '00'
return time.length < 2 ? '0' + time : time
})
}
// 循环扩展原型方法
['formatTime'].forEach(item => {
String.prototype[item] = eval(item)
})
let time = '2020-11-17 09:0:00'
console.log(time.formatTime())
console.log(time.formatTime('{3}时{4}分{5}秒'))
- 简单url拆分
function queryParams(url) {
let obj = {}
let reg = /([^?=&#]+)=([^?=&#]+)/g
url.replace(reg, (...[, $1, $2]) => obj[$1] = $2)
url.replace(/#([^$=?]+)/, (...[, $1]) => obj.hash = $1)
return obj
}
console.log(queryParams('www.baidu.com?123=456&id=888#box'))
- 千分符
function millionmeter(num) {
num += ''
num = num.split('').reverse().join('')
for (let i = 3; i < num.length; i += 4) {
let preV = num.substring(0, i),
next = num.substring(i)
num = preV + ',' + next
}
return num.split('').reverse().join('')
}
let num = 1151781932
console.log(millionmeter(num))
function millionmeter1(num) {
num += ''
num = num.split('').reverse()
for (let i = 3; i < num.length; i += 4) {
num.splice(i,0,',')
}
// reverse 不对原数组产生影响
return num.reverse().join('')
}
console.log(millionmeter1(num))
// 思想:从结尾开始进行前项预查 每三个数字一组,当数字不是三个就进行前一个匹配条件 捕获并且添加逗号 之后剩余所有分组都符合三个一组条件
function millionmeter2(num) {
num += ''
let reg = /\d{1,3}(?=(\d{3})+$)/g
return num = num.replace(reg,content => content + ',')
}
console.log(millionmeter2(num))