跳到主要内容

RegExp

正则表达式中分组可以分为捕获分组和非捕获分组。

捕获分组很简单,用()来表示。

非捕获分组,则基本可以分为五种:

  • (?:) 最基础也是最常见的非捕获分组。

    我们可以使用(?:)来进行非捕获的分组,因为很多时候我们需要使用分组,比如(\d{3})+,但我们又并不想捕获这个分组,这时候就可以使用(?:)了。

  • (?=)零宽正向先行断言(zero-width positive lookahead assertion)。

  • (?!)零宽负向先行断言。

  • (?<=)零宽正向后行断言。

  • (?<!)零宽负向后行断言。

零宽(zero-width)表示着它匹配的是一个位置,就像^$又或者是\b一样,而不是匹配字符。

这里的先行,表示着位置的右边;后行表示着位置的左边。而正向负向,则就是有与无的关系了。

另外默认情况下.+是会进行贪婪匹配,而量词后加上?会进行惰性匹配,比如.+?

常用字符

字符描述
.匹配除换行符 \n 之外的任何单字符
+匹配前面的子表达式一次或多次。
*匹配前面的子表达式零次或多次。
?匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。
\ 转义符。
^匹配输入字符串的开始位置。
$匹配输入字符串的结尾位置。
( )子表达式。
[]子表达式。
\S匹配任何非空白字符。
\s匹配任何空白字符,包括空格、制表符、换页符等等。
\w匹配数字,字母或下划线。
\d匹配数字。
{n}匹配n次。
|指明两项之间的一个选择
\b匹配一个单词边界,即字与空格间的位置。
\B非单词边界匹配。
(x)像下面的例子展示的那样,它会匹配 'x' 并且记住匹配项。其中括号被称为捕获括号。模式 /(foo) (bar) \1 \2/ 中的 '(foo)' 和 '(bar)' 匹配并记住字符串 "foo bar foo bar" 中前两个单词。模式中的 \1\2 表示第一个和第二个被捕获括号匹配的子字符串,即 foobar,匹配了原字符串中的后两个单词。注意 \1\2、...、\n 是用在正则表达式的匹配环节,详情可以参阅后文的 \n 条目。而在正则表达式的替换环节,则要使用像 $1$2、...、$n 这样的语法,例如,'bar foo'.replace(/(...) (...)/, '$2 $1')$& 表示整个用于匹配的原字符串。
(?:x)匹配 'x' 但是不记住匹配项。这种括号叫作非捕获括号,使得你能够定义与正则表达式运算符一起使用的子表达式。看看这个例子 /(?:foo){1,2}/。如果表达式是 /foo{1,2}/{1,2} 将只应用于 'foo' 的最后一个字符 'o'。如果使用非捕获括号,则 {1,2} 会应用于整个 'foo' 单词。
x(?=y)匹配'x'仅仅当'x'后面跟着'y'.这种叫做先行断言。 例如,/Jack(?=Sprat)/会匹配到'Jack'仅当它后面跟着'Sprat'。

当使用构造函数创造正则对象时,需要常规的字符转义规则(在前面加反斜杠 \)。比如,以下是等价的:

var re = new RegExp("\\w+");
var re = /\w+/;

字符串的match可以获取所有匹配正则的结果,但获取不到对应的捕获值。通常想捕获都是用正则表达式的exec方法,但要捕获全部的话需要进行循环exec

es2020新增了一个字符串方法matchAll,相当于循环执行了exec

const reg = /[a-c]/g
const str = 'abc'
for (let i of str.matchAll(reg)) console.log(i)

正则题目

匹配URL参数

// 正则
function getUrlParams(name) {
const reg = new RegExp(`(?:^|&)${name}=([^&]*)(?:$|&)`)
const match = location.search.substr(1).match(reg)
if (match) {
return match[1]
}
}

function getUrl(key) {
let map = {}
let ret
const reg = /(?:^|&)(.*?)=(.*?)(?=&|$)/g
const search = 'name=akara&age=21&type=normal&sex=male'
while (ret !== null) {
ret = reg.exec(search)
if (ret) {
map[ret[1]] = ret[2]
}
}
return map[key]
}

// split
function getUrlParams(name) {
const arr = location.search.substr(1).split('&')
let obj = {}
arr.forEach(item => {
let tempArr = item.split('=')
obj[tempArr[0]] = tempArr[1]
})
return obj[name]
}

匹配cookie参数

function getUrlParams(name) {
const reg = new RegExp(`(\\s|^)${name}=([^;]*)($|;)`) // \s 前面要多一个 \
const match = document.cookie.match(reg)
if (match) {
return match[2]
}
}

域名判断

判断当前域名是否为qq.com,或者其子域名

function isMatch(url) {
return /^https?:\/\/(.+\.)?qq\.com/.test(url)
}
isMatch('http://a.qq.com') // true
isMatch('https://qq.com') // true

电话号码判断

const isPhone = (str) => {
const reg = /^1[34578]\d{9}$/g
return reg.test(str)
}

驼峰化

aaa-bbb-ccc转换为驼峰aaaBbbCcc

function toCamel(str) {
return str.replace(/-\w/g, (s) => {
return s.slice(1).toUpperCase()
})
}
// \w 匹配数字,字母与下划线

数字的千分位分割

// 正则,注意使用到了?=先行断言,?:为非捕获,可加可不加
function format(num) {
return num.toString().replace(/(?=(?<!^)(?:\d{3})+$)/g, ',')
}

// function format(num) {
// const reg = /\d{1,3}(?=(?:\d{3})+$)/g
// return num.toString().replace(reg, '$&,')
// }

// const format = (number) => {
// const str = number + ''
// return str.replace(/(?=(?!^)(\d{3})+$)/g, ',')
// // return str.replace(/\d{1,3}(?=(\d{3})+$)/, '$&,')
// }

// 也可以用toLocaleString轻松实现
function format(num) {
return num.toLocaleString()
}

// 或者用数字来分割
function format (num) {
let arr = []
while (num >= 1000) {
let value = num % 1000
num = num / 1000
if (value >= 100) {
value = '' + value
} else if (100 > value && value >= 10 ){
value = `0${value}`
} else {
value = `00${value}`
}

arr.unshift(value)
}
num = '' + num
arr.unshift(num)
return arr.join(',')
}

替换元素

把非P元素替换成P元素 <div></div> => <p></p>

const replaceP = (str) => {
return str.replace(/<(\/)?.*?>/g, '<$1p>')
}

文章出现最多的单词

const wordOfArticle = (str) => {
const reg = /(?=\b)(.+?)(?=\b)/g
console.log(str.match(reg))
}