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 表示第一个和第二个被捕获括号匹配的子字符串,即 foo 和 bar ,匹配了原字符串中的后两个单词。注意 \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))
}