最近因为项目中有些地方需要正则,给我难受坏了,因为这东西看了就忘,用到就得查,属实难为人了,那今天菜鸟又粗略整理了一遍,以便之后用到翻看。文中有些例子和分类思路用到了网站中js知识的内容,如有更好的建议,诚恳发言,感谢大家!!!
正则表达式
js
let regexp = new RegExp(pattern[模式],modifiers[修饰符])
let regexp = /pattern/modifiers
【注】当使用构造函数创造正则对象时,需要常规的字符转义规则(在前面加反斜杠 \)
var re = new RegExp("\\w+");
var re = /\w+/;
修饰符
修饰符 | 说明 |
---|---|
i | 搜索时不区分大小写:A和a之间没区别 |
g | 搜索时会寻找所有的匹配项(全局匹配) |
m | 多行匹配 |
s | 启用"dotall"模式,允许点. 匹配换行符\n |
u | 开启完整的Unicode支持,能够正确处理四个字节的UTF-16编码 |
y | 粘滞模式,同g修饰符类似,都是全局匹配,但是y必须从剩余字符的第一个位置开始匹配,否则退出匹配 |
字符串方法
str.match(regexp)
- match 查找匹配项并以
数组
形式返回,如果未查找到则返回null
- 返回数组的内容依赖于regexp是否具有全局标志g
js
let str = "We can do it, but we cannot say";
console.log(str.match(/we/)); // ['we',index: 18,input: 'We can do it, but we cannot say',groups: undefined]
console.log(str.match(/we/gi)); //[ 'We', 'we' ]
console.log(str.match(/hello/)); //null
// 【注】如果没有匹配项,则返回null,所以在需求为不匹配则返回空数组的应用中,多加一层判断
let result = 'JavaScript'.match(/vue/) || []
console.log(result, !result.length); // [] true
str.matchAll(regexp)
- matchAll 它返回的不是数组,而是一个
可迭代对象
- (正则表达式的修饰符必须加g)它将每个匹配项以包含组的数组的形式返回
- 如果没有匹配项,则返回的不是null,而是一个
空的可迭代对象
js
let str = "map tap nap"
let regexp = /[a-z]ap/g
console.log(Array.from(str.matchAll(regexp)));
// [
// ['map', index: 0, input: 'map tap nap', groups: undefined],
// ['tap', index: 4, input: 'map tap nap', groups: undefined],
// ['nap', index: 8, input: 'map tap nap', groups: undefined]
// ]
let regexp1 = /mn/g
console.log(Array.from(str.matchAll(regexp1))); // []
regexp.exec(str)
- exec 查找匹配项并以
数组
形式返回,如果未查找到则返回null
- 无论有无g修饰,
都返回第一个匹配的字符串
;该方法还可以返回子匹配项
js
let str = "We can do it, but we Can not say net";
let regexp = /can/g
let regexp1 = /hello/g
let regexp2 = /n([a-z]+)t/g
console.log(regexp.exec(str)); //['can',index: 3,input: 'We can do it, but we Can not say net',groups: undefined]
console.log(regexp1.exec(str)); //null
console.log(regexp2.exec(str)); //['not','o',index: 25,input: 'We can do it, but we Can not say net',groups: undefined]
// exec只返回第一个匹配的字符串,如果想返回全部(借助while和g参数实现)
while (res = regexp2.exec(str)) {
console.log(res);
// [
// 'not',
// 'o',
// index: 25,
// input: 'We can do it, but we Can not say net',
// groups: undefined
// ]
// [
// 'net',
// 'e',
// index: 33,
// input: 'We can do it, but we Can not say net',
// groups: undefined
// ]
}
str.search(regexp)
- search 返回第一个匹配的字符串所在
位置
,偏移量从0
开始
js
let str = "We can do it, but we Can not say net";
console.log(str.search(/can/g)); //3
console.log(str.search(/we/i)); //0
str.replace(regexp,replacement)
- replace 返回结果为
替换第一个或所有匹配项之后新的字符串
js
let str = "We can do it, but we can not say";
console.log(str.replace(/we/, '123')); //We can do it, but 123 can not say
console.log(str.replace(/we/gi, '123')); //123 can do it, but 123 can not say
第二个参数replacement是字符串,可以在其中使用特殊的字符组合来对匹配项进行插入:
符号 | 说明 |
---|---|
$& | 插入整个匹配项 |
$` | 插入字符串中匹配项之前的字符串部分 |
$' | 插入字符串中匹配项之后的字符串部分 |
$n | n为数字,则插入第n个分组的内容 |
$ | 插入带给定name的括号内的内容 |
$$ | 插入字符$ |
js
let str = "We can do it, but we can not say";
console.log(str.replace(/say/, "$&, you are so cute.")); // We can do it, but we can not say, you are so cute.
str.replaceAll(regexp,replacement)
- replaceAll 返回
替换所有匹配项之后的新的字符串
- 当使用一个 regexp 时,
必须设置全局("g")标志
, 否则,它将引发 TypeError:"必须使用全局 RegExp 调用 replaceAll"。`
js
let str = "We can do it, but we can not say";
console.log(str.replaceAll(/can/g, 123)); //We 123 do it, but we 123 not say
regexp.test(str)
- test 用于寻找至少一个匹配项,找到了返回
true
,否则返回false
js
let str = "We can do it, but we can not say";
let regexp = /NOT/i
let regexp1 = /NOT/
console.log(regexp.test(str)); // true
console.log(regexp1.test(str)); // false
str.split(separator[,limit])
- split 用于把一个字符串
分割成字符串数组
limit
用于限制分割后数组的长度
js
let str = "a,b,c"
let regexp = /,/
console.log(str.split(regexp)); // [ 'a', 'b', 'c' ]
let str1 = "aa,bb:cc#dd@ee"
let regexp1 = /[,:#@]/
console.log(str1.split(regexp1, 2)); // [ 'aa', 'bb' ]
console.log(str1.split(regexp1, 3)); // [ 'aa', 'bb', 'cc' ]
console.log(str1.split(regexp1)); // [ 'aa', 'bb', 'cc', 'dd', 'ee' ]
语法
字符类
示例 | 说明 |
---|---|
. | 匹配除换行符以外的任何字符 |
\d | 匹配数字:[0-9] |
\D | 匹配非数字:[^0-9] |
\s | 匹配空格字符:[\t\n\v\f\r] |
\S | 匹配非空格字符:[^\t\n\v\f\r] |
\w | 匹配单字字符:[A-Za-z0-9_] |
\W | 匹配非单字字符:[^A-Za-z0-9_] |
js
// 提取电话中纯数字
let str = "+7(903)-123-45-67"
let regexp = /\d/g
console.log(str.match(regexp).join('')); //79031234567
console.log(str.replace(/\D/g, "")); //79031234567
// 正则同时包含常规符号和字符类
let str1 = "Are you learning ES6?"
let regexp1 = /ES\d/
console.log(str1.match(regexp1)); // [ 'ES6', index: 17, input: 'Are you learning ES6?', groups: undefined ]
// 正则同时使用多个字符类进行匹配
console.log(str1.match(/\s\w\w\d/)); // [' ES6',index: 16,input: 'Are you learning ES6?',groups: undefined]
// 点(.)匹配除换行符之外的任何字符
let regexp2 = /E.6/
console.log('ES6'.match(regexp2)); // [ 'ES6', index: 0, input: 'ES6', groups: undefined ]
console.log('E46'.match(regexp2)); // [ 'E46', index: 0, input: 'E46', groups: undefined ]
console.log('E 6'.match(regexp2)); // [ 'E 6', index: 0, input: 'E 6', groups: undefined ]
console.log('E$6'.match(regexp2)); // [ 'E$6', index: 0, input: 'E$6', groups: undefined ]
console.log('E6'.match(regexp2)); //null
console.log('E\n6'.match(regexp2)); //null
// 带有修饰符"s"时点字符类匹配任何字符
let regexp3 = /E.6/s
console.log('E\n6'.match(regexp3)); //[ 'E\n6', index: 0, input: 'E\n6', groups: undefined ]
锚点
【注】锚点本身宽度为零,它们并不匹配任一个具体的字符
示例 | 说明 |
---|---|
^Demo | 在字符串或内部行的开头匹配"Demo" |
Demo$ | 在字符串或内部行的结尾匹配"Demo" |
js
// 开头、结尾匹配
let str = "love and Peace"
console.log(/^love/.test(str)); // true
console.log(/Peace$/.test(str)); // true
// 开头、结尾+多行匹配模式m
let str2 = `1respect
2encourage
3praise`
console.log(str2.match(/^\d/g)); // [ '1' ]
console.log(str2.match(/^\d/gm)); // [ '1', '2', '3' ]
词边界 \b
有三种不同的位置可作为词边界:
- 在字符串开头,如果第一个字符是单词字符
\w
。 - 在字符串中的两个字符之间,其中一个是单词字符
\w
,另一个不是。 - 在字符串末尾,如果最后一个字符是单词字符
\w
。
js
let str = "hello, world!"
console.log(str.match(/\bhello\b/)); // [ 'hello', index: 0, input: 'hello, world!', groups: undefined ]
console.log(str.match(/\bworld\b/)); // [ 'world', index: 7, input: 'hello, world!', groups: undefined ]
console.log(str.match(/\bhell\b/)); // null
console.log(str.match(/\bworld!\b/)); // null
console.log("12 34 56".match(/\b\d\d\b/g)); // [ '12', '34', '56' ]
转义,特殊字符
要将特殊字符转化为常规字符,需在其前面加反斜杠
- 常用的特殊字符有:
[] {} () \ ^ $ . | ? * +
js
let str = "[注] 章节5.1部分有?的内容+"
console.log(str.match(/\d\.\d/)); //[ '5.1', index: 6, input: '[注] 章节5.1部分有?的内容+', groups: undefined ]
console.log(str.match(/\?/)); //[ '?', index: 12, input: '[注] 章节5.1部分有?的内容+', groups: undefined ]
console.log(str.match(/\[\W+\]/)); //[ '[注]', index: 0, input: '[注] 章节5.1部分有?的内容+', groups: undefined ]
集合和范围
- 集合:
[abc]
表示3个字符中的任何一个:'a'、'b'、'c';虽然集合有多个字符,但它们在匹配中只会对应其中一个
。 - 范围:
[a-z]
表示从'a'到'z'范围内的字符;[0-9A-Z]
两个范围,字符要么0-9范围内的数字,要么A-Z范围内的字母。 - 排除范围:
[^...]
通过在开头添加插入符号,来表示匹配所有除了给定字符
之外的任意字符。 - [...]的转义: 插入符号
^
仅在开头会被转义(表示排除),右方括号]
总是会被转义; 除了在方括号中有特殊含义的字符外,其它所有特殊字符都是允许不转义的。
js
let str = 'map, Tap, nap'
// 范围
let regexp = /[tm]ap/gi
console.log(str.match(regexp)); // [ 'map', 'Tap' ]
// 集合
let regexp1 = /[a-n]ap/gi
console.log(str.match(regexp1)); // [ 'map', 'nap' ]
// 排除范围
let regexp2 = /[^a-n]ap/gi
console.log(str.match(regexp2)); // [ 'Tap' ]
// [...]中的转义
let str1 = '1+2-[4*5]/(6.2-1)'
let regexp3 = /[-().^+*/[]]/g;
console.log(str1.match(regexp3)); // null
let regexp4 = /[^-().+*/[]]/g
console.log(str1.match(regexp4)); // [ '5]' ]
let regexp5 = /[^-().+*/[\]]/g
console.log(str1.match(regexp5)); // ['1', '2', '4','5', '6', '2','1']
量词
示例 | 说明 |
---|---|
{5} | 匹配5个 |
{3,5} | 匹配3-5个 |
{3,} | 匹配3个及以上 |
+ | 代表"一个或多个",同{1,} |
* | 代表"零个或多个",同{0,} |
? | 代表"零个或一个",同{0,1} |
js
let str = "please select from 12345 12 1234 123456 97 987 9887 98887 to finish this task."
let regexp = /\d{5}/
console.log(str.match(regexp)); // 12345
let regexp1 = /\d{3,4}/
console.log(str.match(regexp1)); // 1234
let regexp2 = /\d{6,}/
console.log(str.match(regexp2)); // 123456
let regexp3 = /\d+/g
console.log(str.match(regexp3)); // [ '12345', '12', '1234', '123456' ]
let regexp4 = /9\d?7/g
console.log(str.match(regexp4)); // [ '97', '987' ]
let regexp5 = /9\d+7/g
console.log(str.match(regexp5)); // [ '987', '9887', '98887' ]
let regexp6 = /9\d*7/g
console.log(str.match(regexp6)); // [ '97', '987', '9887', '98887' ]
贪婪量词和惰性量词
js
let str = 'Who can tell "impel" from "compel"'
// 在贪婪模式下(默认情况),两次都会尽可能多地重复
let regexp = /".+"/g
console.log(str.match(regexp)); // [ '"impel" from "compel"' ]
// 惰性模式,表示重复最少的次数;通过在量词后面添加'?'来实现,变成'*?'或'+?'或'??'
let regexp1 = /".+?"/g
console.log(str.match(regexp1)); // [ '"impel"', '"compel"' ]
// 换一种非惰性提取单词的方式
let regexp2 = /"[^"]+"/g
console.log(str.match(regexp2)); // [ '"impel"', '"compel"' ]
捕获组
模式的一部分可以用括号括起来(...),这被称为"捕获组" 这有两个影响:
- 它允许将匹配的一部分作为结果数组中的单独项
- 如果我们将量词放在括号后,则它将括号视为一个整体
js
let str = "Gogogo right now! Google goose"
// 匹配重复括号内内容
let regexp = /(go)+/gi
console.log(str.match(regexp)); // [ 'Gogogo', 'Go', 'go' ]
let str1 = "cn.bing.com www.baidu.com my@mail.com work@edu.com"
// 匹配域名
let regexp1 = /(\w+\.)+\w+/g
console.log(str1.match(regexp1)); // [ 'cn.bing.com', 'www.baidu.com', 'mail.com', 'edu.com' ]
// 电子邮件
let regexp2 = /[-.\w]+@([\w-]+\.)+[\w-]+/g
console.log(str1.match(regexp2)); // [ 'my@mail.com', 'work@edu.com' ]
let str2 = `<span class="desc"></span>`
// 嵌套组
let regexp3 = /<(([a-z]+)\s*([^>]*))>/
console.log(str2.match(regexp3)); // ['<span class="desc">','span class="desc"','span','class="desc"',index: 0, input: '<span class="desc"></span>', groups: undefined]
// 可选组
console.log('a'.match(/a(b)?(c)?/)); // [ 'a', undefined, undefined, index: 0, input: 'a', groups: undefined ]
console.log('ac'.match(/a(b)?(c)?/)); // [ 'ac', undefined, 'c', index: 0, input: 'ac', groups: undefined ]
// 带有组搜索所有匹配项:matchAll
/**
* 它返回的不是数组,而是一个可迭代对象
* 当存在修饰符g时,它将每个匹配项以包含组的数组的形式返回
* 如果没有匹配项,则返回的不是null,而是一个空的可迭代对象
*/
let str3 = '<h1> <h2>'
console.log(Array.from(str3.matchAll(/<(.*?)>/gi)));
// [
// ['<h1>', 'h1', index: 0, input: '<h1> <h2>', groups: undefined],
// ['<h2>', 'h2', index: 5, input: '<h1> <h2>', groups: undefined]
// ]
// 命名组
let regexp4 = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;
let str4 = "2019-04-30";
console.log(str4.match(regexp4));
// [
// '2019-04-30',
// '2019',
// '04',
// '30',
// index: 0,
// input: '2019-04-30',
// groups: [Object: null prototype] {
// year: '2019',
// month: '04',
// day: '30'
// }
// ]
// 替换中的捕获组
let str5 = 'John Bull'
let regexp5 = /(\w+) (\w+)/
console.log(str5.replace(regexp1, '$2,$1')); // Bull,John
// 非捕获组
let str6 = "Gogogo John!"
let regexp6 = /(?:go)+ (\w+)/i
console.log(str6.match(regexp6)); // ['Gogogo John','John',index: 0, input: 'Gogogo John!', groups: undefined]
选择 (OR) |
js
let str = "First HTML appeared, then CSS, then JavaScript"
let regexp = /html|css|java(script)?|vue/gi
console.log(str.match(regexp)); // [ 'HTML', 'CSS', 'JavaScript' ]
前瞻断言与后瞻断言
- 前瞻断言:
x(?=y)
它表示"仅在后面是y
时匹配x
"。'x'和'y'可以是任何模式 - 否定的前瞻断言:
x(?!y)
它表示搜索x
,但前提是后面没有y
- 后瞻断言:
(?<=y)x
它表示匹配x
,仅在前面是y
的情况下 - 否定的后瞻断言:
(?<!y)x
它表示匹配x
,仅在前面不是y
的情况下 - 捕获组: 一般来说断言的内容不会成为结果的一部分,在某些情况下,我们可能还想捕获前瞻断言和后瞻断言所匹配的内容,或者部分内容,只需要将该部分包装在额外的括号中。
js
let str = `1 turkey costs 30€`
// 前瞻断言
console.log(str.match(/\d+(?=€)/)); //[ '30', index: 15, input: '1 turkey costs 30€', groups: undefined ]
// 否定前瞻断言
console.log(str.match(/\d+(?!€)/)); //[ '1', index: 0, input: '1 turkey costs 30€', groups: undefined ]
let str1 = `2 turkey costs $30`
// 后瞻断言
console.log(str1.match(/(?<=\$)\d+/)); //[ '30', index: 16, input: '2 turkey costs $30', groups: undefined ]
// 否定后瞻断言
console.log(str1.match(/(?<!\$)\b\d+/g)); // [ '2' ]
// 捕获组
let regexp = /(?<=(\$|£))\d+/
console.log(str1.match(regexp));
// [
// '30',
// '$',
// index: 16,
// input: '2 turkey costs $30',
// groups: undefined
// ]
Unicode 属性 \p{...}
-
字母(Letter)
L
:- 小写(lowercase)
Ll
, - 修饰(modifier)
Lm
, - 首字母大写(titlecase)
Lt
, - 大写(uppercase)
Lu
, - 其它(other)
Lo
。
- 小写(lowercase)
-
数字(Number)
N
:- 十进制数字(decimal digit)
Nd
, - 字母数字(letter number)
Nl
, - 其它(other)
No
。
- 十进制数字(decimal digit)
-
标点符号(Punctuation)
P
:- 连接符(connector)
Pc
, - 横杠(dash)
Pd
, - 起始引号(initial quote)
Pi
, - 结束引号(final quote)
Pf
, - 开(open)
Ps
, - 闭(close)
Pe
, - 其它(other)
Po
。
- 连接符(connector)
-
标记(Mark)
M
(accents etc):- 间隔合并(spacing combining)
Mc
, - 封闭(enclosing)
Me
, - 非间隔(non-spacing)
Mn
。
- 间隔合并(spacing combining)
-
符号(Symbol)
S
:- 货币(currency)
Sc
, - 修饰(modifier)
Sk
, - 数学(math)
Sm
, - 其它(other)
So
。
- 货币(currency)
-
分隔符(Separator)
Z
:- 行(line)
Zl
, - 段落(paragraph)
Zp
, - 空格(space)
Zs
。
- 行(line)
-
其它(Other)
C
:- 控制符(control)
Cc
, - 格式(format)
Cf
, - 未分配(not assigned)
Cn
, - 私有(private use)
Co
, - 代理伪字符(surrogate)
Cs
。
- 控制符(control)
js
// 需要使用 "u" 修饰符进行正则匹配
// 十六进制匹配
let regexp1 = /x\p{Hex_Digit}\p{Hex_Digit}/u
console.log("number:xAf".match(regexp1)); // [ 'xAf', index: 7, input: 'number:xAf', groups: undefined ]
// 中文字符
let regexp2 = /\p{sc=Han}/gu
console.log("Hello Привет 你好 123_456".match(regexp2)); // [ '你', '好' ]
// 货币
let regexp3 = /\p{Sc}\d/gu
console.log('Prices:$2,€1, ¥9'.match(regexp3)); // [ '$2', '€1', '¥9' ]
结尾
好了,到这里常用的规则已经复习差不多了,阿呆用两个例子草草收尾吧,如果大佬有幸阅到,还请留下您美好的建议!!!
js
let str = `<div class="out"><p class="inner"><span class="text"></span></p></div><div id="out"><p style="color:red;"><span style="position:absolute"></span></p></div>`
let regexp = /\s*(class|id|style)=("[a-z]+:?[a-z]+;?")/g
console.log(str.replaceAll(regexp, '')); // <div><p><span></span></p></div><div><p><span></span></p></div>
let str1 = "or Or oR OR Ordinary doctor sorry"
let regexp1 = /\bor\s+/gi
console.log(str1.replaceAll(regexp1, ' OR ')); // OR OR OR OR Ordinary doctor sorry