JavaScript正则表达式RegExp
- 个人学习复制粘贴、总结而来
不看就忘,常复习!
@本文档由以下网站和B站视频总结
创建RegExp对象:
-
字面量 reg=/\s/ afgls
字面量正则文本部分包含在一对斜杠
/
之间字面量参数不使用引号
后面一个/后跟匹配标志
空格在正则中时一个字符
-
正则表达式转换函数
RegExp(pattern [, flags])
patterm:要匹配的字符串模式
flag可选参数:可选的标志字符串
标识 模式 描述 g
全局(global)模式 即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止 i
忽略大小写(case-insensitive)模式 即在确定匹配项时忽略模式与字符串的大小写 m
多行(multiline)模式 即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项
它只影响^
和$
的行为。s 启用 “dotall” 模式 允许点 .
匹配换行符\n
这下.能匹配任意字符了u 开启完整的 Unicode 支持 该修饰符能够正确处理代理对
如果集合[]中有代理对(surrogate pairs),则需要标志u
才能使它们正常工作。y 粘滞模式 在文本中的确切位置搜索 new RegExp('at', 'gim');// **g**全局 **i**不区分大小写 **m**多行模式会匹配\n换行
-
由于正则表达式字面量并不支持变量,所以如果正则表达式中出现变量只能使用
RegExp
构造函数以字符串拼接的形式,将变量拼接到RegExp
构造函数的参数中。// example let variable = 'low'; let regexp = new RegExp('^Hel' + variable + 'orld$', 'gim');//在字符串拼接正则需要注意使用转义特殊符号 console.log(regexp); // /^Helloworld$/gim
RegExp对象属性
从内置对象RegExp继承的属性 实例可用
原型属性 | 描述 |
---|---|
RegExp.prototype.constructor | 创建该正则对象的构造函数 |
RegExp.prototype.global | 是否开启全局匹配,也就是匹配目标字符串中所有可能的匹配项,而不是只进行第一次匹配。 |
RegExp.prototype.ignoreCase | 在匹配字符串时是否要忽略字符的大小写。 |
RegExp.prototype.lastIndex | 整数,表示开始搜索后下一个匹配项的字符索引位置,从 0 算起。 |
RegExp.prototype.multiline | 是否开启多行模式匹配(影响 ^ 和 $ 的行为) |
RegExp.prototype.source | 正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回 |
静态属性只能由RegExp.xxx调用,这些属性基于所执行的最近一次正则表达式操作而变化。
长属性名 | 短属性名 | 说明 |
---|---|---|
input | $_ | 最近一次要匹配的字符串 |
lastMatch | $& | 最近一次的匹配项 |
lastParen | $+ | 最近一次匹配的捕获组 |
leftContext | $` | input 字符串中 lastMatch 之前的文本 |
multiline | $* | 布尔值,表示是否所有表达式都使用多行模式 |
rightContext | $’ | input 字符串中 lastMatch 之后的文本 |
// test()用于测试一个字符串是否匹配某个正则表达式,并返回一个布尔值
let text = 'this has been a short summer';
let regexp = /(.)hort/g;
if (regexp.test(text)) {
console.log(RegExp.input); // 'this has been a short summer'
console.log(RegExp.leftContext); // 'this has been a '
console.log(RegExp.rightContext); // ' summer'
console.log(RegExp.lastMatch); // 'short'
console.log(RegExp.lastParen); // 's'
console.log(RegExp.multiline); // false
console.log(RegExp['$_']); // 'this has been a short summer'
console.log(RegExp['$`']); // 'this has been a '
console.log(RegExp["$'"]); // ' summer'
console.log(RegExp['$&']); // 'short'
console.log(RegExp['$+']); // 's'
console.log(RegExp['$*']); // false
}
RegExp原型方法:
方法 | 描述 |
---|---|
RegExp.prototype.exec() | 在一个指定字符串中执行一个搜索匹配。返回一个结果数组或 null 。 |
RegExp.prototype.test() | 执行一个检索,用来查看正则表达式与指定的字符串是否匹配。返回 true 或 false 。 |
1、regExpObject.exec(str);
// Match "quick brown" followed by "jumps", ignoring characters in between
// Remember "brown" and "jumps"
// Ignore case
let result = regexp.exec('The Quick Brown Fox Jumps Over The Lazy Dog');
let regexp = /quick\s(brown).+?(jumps)/gi;
regexp[0]=匹配的字符串
regexp[1],。。。[n]或者RegExp.$1…9括号中的捕获组
regexp.index 匹配的字符串的第一个在字符串中的索引
regexp.input 原始字符串
2、regExpObject.test(str);
let str = 'hello world!';
let result = /^hello/.test(str);
console.log(result); // true
RegExp语法:
元字符:
在正则表达式中具有特殊意义的专用字符,可以用来规定其前导字符(即位于元字符前面的字符)在目标对象中的出现模式。
元字符 | 名称 | 匹配对象 |
---|---|---|
. | 点号 | 单个任意字符(除回车 \r 、换行 \n 、行分隔符 \u2028 和段分隔符 \u2029 外) |
[] | 字符组 | 列出的单个任意字符 |
[^] | 排除型字符组 | 未列出的单个任意字符 |
? | 问号 | 匹配 0 次或 1 次 |
* | 星号 | 匹配 0 次或多次 |
+ | 加号 | 匹配 1 次或多次 |
{min,max} | 区间量词 | 匹配至少 min 次,最多 max 次 |
^ | 脱字符 | 行的起始位置 |
$ | 美元符 | 行的结束位置 可以设置$=“” |
` | ` | 竖线 |
() | 括号 | 限制多选结构的范围,标注量词作用的元素,为反向引用捕获文本 通过$1到$9获取捕获组 |
\1,\2... | 反向引用 | 匹配之前的第一、第二…组括号内的表达式匹配的文本 |
1、选择!!OR 在()中可以使用 | 进行或者判断 /- | x/ -符号或者x 和方括号类似,但是方括号只允许字符或字符类。选择允许任何表达式
let regexp = /([01]\d|2[0-3]):[0-5]\d/g;
alert("00:00 10:10 23:59 25:99 1:2".match(regexp)); // 00:00,10:10,23:59
let str = "html and css";
let result = str.replace(/html|css/gi, str => str.toUpperCase()); |也可以直接写在//中
alert(result); // HTML and CSS
2、^$可以匹配到 ""空字符串
3、反向引用:\1,\2...
不使用123可以选择命名组的name 在捕获组中介绍
正则表达式引擎会找到第一个引号
(['"])
并记住其内容。那是第一个捕获组。在模式中
\1
表示“找到与第一组相同的文本”,在我们的示例中为完全相同的引号。与此类似,
\2
表示第二组的内容,\3
—— 第三分组,依此类推。let str = `He said: "She's the one!".`; let regexp = /['"](.*?)['"]/g; // 不是我们想要的结果 alert( str.match(regexp) ); // "She' let str = `He said: "She's the one!".`; let regexp = /(['"])(.*?)\1/g; alert( str.match(regexp) ); // "She's the one!"
简单字符组:
字符 | 描述 |
---|---|
\d | 数字,等同于 [0-9] |
\D | 非数字,等同于 [^0-9] |
\s | 空白字符,等同于 [\f\n\r\t\u000B\u0020\u00A0\u2028\u2029] |
\S | 非空白字符,等同于 [^\f\n\r\t\u000B\u0020\u00A0\u2028\u2029] |
\w | 字母、数字、下划线,等同于 [0-9A-Za-z_] |
\W | 非字母、数字、下划线,等同于 [^0-9A-Za-z_] |
字符类是某些字符集合的简写
任意字符:
字符 | 描述 |
---|---|
. | 表示除回车 (\r) 、换行 (\n) 、行分隔符 (\u2028) 和段分隔符 (\u2029) 以外的任意字符。 其实 [\s\S] 、[\w\W] 、[\d\D] 更可以表示任意字符。 |
转义字符:
字符 | 描述 |
---|---|
\ + 元字符 | 匹配元字符 |
\ + ] 或 \ + } | 右方括号和右花括号无需转义 |
\ + 非元字符 | 表示一些不能打印的特殊字符 \0 \t \n \v \f \r \xnm \uxxxx \cx [\b] |
\ + 除上述其他字符 | 默认情况匹配此字符 |
- 要在字面意义上搜索特殊字符
[ \ ^ $ . | ? * + ( )
,我们需要在它们前面加上一个反斜杠\
(“转义它们”)。 - 如果在
/.../
内(但不在new RegExp
内),我们还需要转义/
。 - 当将字符串传递给给
new RegExp
时,我们需要双反斜杠\\
,因为字符串引号会消耗一个反斜杠。
字符集合:
字符 | 描述 |
---|---|
[xyz] | 一个字符集合,也叫字符组。匹配集合中任意一个字符。 |
[^xyz] | 一个反义或补充字符集,也叫反义字符组。匹配任意不包括括号内的字符(排除)。 |
1、 在方括号,我们可以使用绝大多数特殊字符而无需转义
2、字符集[.]就是匹配.符号
3、^
仅在开头会被转义 ]
总是会被转义 不然没形成[]
4、可以使用 -
指定一个范围。 a-z 0-9 A-z 根据ASCII码
5、破折号 '-'
在方括号中有特殊含义,但只有当它位于其它字符之间而不是开头或结尾时这个含义才会起作用,所以我们不需要对其进行转义。
数量词:
字符 | 描述 |
---|---|
x* | 相当于 x{0,} (匹配任意多次) |
x+ | 相当于 x{1,} (匹配至少一次) |
x? | 相当于 x{0,1} (不匹配或匹配一次) |
x*? 或 x+? | 相当于 * 和 + 字符,然而匹配的是最小可能匹配 |
x(?=y) | 只有当 x 后面紧跟着 y 时,才匹配 x 。(了解详情请看 环视) |
x(?!y) | 只有当 x 后面不是紧跟着 y 时,才匹配 x 。(了解详情请看 环视) |
`x | y(这里是没有 ` 的) |
x{n} | 匹配 n 次(n 为正整数) |
x{n,m} | 匹配至少 n 次,最多 m 次(n 和 m 为正整数) |
x{n,} | 匹配至少 n 次(n 为正整数) |
1、贪婪模式:
默认情况下,正则表达式引擎会尝试尽可能多地重复量词字符。例如,\d+
会消耗所有可能的字符。当无法消耗更多(在尾端没有更多的数字或字符串)时,然后它再匹配模式的剩余部分。如果没有匹配,则减少重复的次数(回溯),并再次尝试。
2、惰性模式:
正如我们所见,惰性模式并不是贪婪搜索的“灵丹妙药”。另一种方式是使用排除项“微调”贪婪搜索,如模式 "[^"]+"
。
词边界:
词边界 \b
是一种检查,就像 ^
和 $
一样。
当正则表达式引擎(实现正则表达式搜索的程序模块)遇到 \b
时,它会检查字符串中的位置是否是词边界。
有三种不同的位置可作为词边界:
-
在字符串开头,如果第一个字符是单词字符
\w
。 -
在字符串中的两个字符之间,其中一个是单词字符
\w
,另一个不是。 -
在字符串末尾,如果最后一个字符是单词字符
\w
。alert( "Hello, Java!".match(/\bJava\b/) ); // Java alert( "Hello, JavaScript!".match(/\bJava\b/) ); // null
词边界
\b
不适用于非拉丁字母
捕获组:
模式的一部分可以用括号括起来 (...)
。这被称为“捕获组(capturing group)”。
- 它允许将匹配的一部分作为结果数组中的单独项。
- 如果我们将量词放在括号后,则它将括号视为一个整体。
嵌套组:括号可以嵌套。在这种情况下,编号也从左到右。
可选组:即使组是可选的并且在匹配项中不存在(例如,具有量词 (...)?
),也存在相应的 result
数组项,并且等于 undefined
。
命名组:在左括号后紧跟着放置 ?<name>
即可完成对括号的命名。
替换中的捕获组:用到字符串的replace方法
让我们能够替换 str
中 regexp
的所有匹配项的方法 str.replace(regexp, replacement)
允许我们在 replacement
字符串中使用括号中的内容。这使用 $n
来完成,其中 n
是组号。
非捕获组:
有时我们需要用括号才能正确应用量词,但我们不希望它们的内容出现在结果中。
可以通过在开头添加 ?:
来排除组。
RegExp高级:
前瞻断言和后瞻断言:
模式 | 类型 |
---|---|
X(?=Y) | 肯定的前瞻断言 |
X(?!Y) | 否定的前瞻断言 |
(?<=Y)X | 肯定的后瞻断言 |
(?<!Y)X | 否定的后瞻断言 |
有时我们只需要为一个模式找到那些在另一个模式之后或之前的匹配项。
有一种特殊的语法,称为“前瞻断言(lookahead)”和“后瞻断言(lookbehind)”。
1、前瞻语法:
x(?=y)
,它表示“仅在后面是 Y
时匹配 X
”。There may be any pattern instead of X
and Y
.
那么对于一个后面跟着 €
的整数,正则表达式应该为:\d+(?=€)
。
let str = "1 turkey costs 30€";
alert( str.match(/\d+(?=\s)(?=.*30)/) ); // 1 可以嵌套连续判定 成功继续判断 全部满足则匹配
2、否定的前瞻语法:
假设我们想要一个数量,而不是来自同一字符串的价格。那是一个数字 \d+
,后面不是 €
。
为此,我们可以使用否定的前瞻断言。
语法是:X(?!Y)
,意思是“搜索 X
,但前提是后面没有 Y
”。
let str = "2 turkeys cost 60€";
alert( str.match(/\d+\b(?!€)/g) ); // 2(60€ 不匹配)
3、后瞻断言
前瞻断言允许添加一个“后面要跟着什么”的条件判断。
后瞻断言也类似,只不过它是在相反的方向上进行条件判断。也就是说,它只允许匹配前面有特定字符串的模式。
语法为如下:
- 肯定的后瞻断言:
(?<=Y)X
,匹配X
,仅在前面是Y
的情况下。 - 否定的后瞻断言:
(?<!Y)X
,匹配X
,仅在前面不是Y
的情况下。
let str = "1 turkey costs $30";
// 美元符号被转义 \$
alert( str.match(/(?<=\$)\d+/) ); // 30(跳过了仅仅是数字的值)
let str = "2 turkeys cost $60";
alert( str.match(/(?<!\$)\b\d+/g) ); // 2(价格不匹配)
3、与捕获组结合:
一般来说,前瞻断言和后瞻断言括号中的内容不会成为结果的一部分。
例如,在模式 \d+(?!€)
中,€
符号就不会出现在匹配结果中。这是很自然的事:我们寻找一个数字 \d+
,而 (?=€)
只是一个测试,表示要匹配的数字后面应该紧跟着 €
字符。
但在某些情况下,我们可能还想捕获前瞻断言和后瞻断言所匹配的内容,或者部分内容。这也是可行的。只需要将该部分包装在额外的括号中。
在下面的示例中,货币符号 (€|kr)
和金额一起被捕获了:
let str = "1 turkey costs 30€";
let regexp = /\d+(?=(€|kr))/; // €|kr 两侧有额外的括号
alert( str.match(regexp) ); // 30, € 前瞻
let str = "1 turkey costs $30";
let regexp = /(?<=(\$|£))\d+/;
alert( str.match(regexp) ); // 30, $ 后瞻
灾难性回溯:
不想看了,涉及大量数据处理,服务器挂起什么的情况 https://zh.javascript.info/regexp-catastrophic-backtracking
有些正则表达式看起来很简单,但执行起来耗时却非常长,甚至会导致 JavaScript 引擎“挂起”。
大多数开发者迟早会遇到这样的情况。典型的症状就是 —— 正则表达式有时可以正常工作,但对于某些字符串,它会消耗 100% 的 CPU 算力,出现“挂起”的现象。
在这种情况下,Web 浏览器会建议终止脚本并重新加载页面。这显然不是我们愿意看到的。
对于服务器端 JavaScript,这样的正则表达式可能会挂起服务器进程,这甚至更糟。所以我们绝对应该研究研究它。
正则在字符串的方法:
1、str.match(regexp)
方法在字符串 str
中寻找 regexp
的所有匹配项。
- 如果正则表达式具有修饰符
g
,它返回一个由所有匹配项所构成的数组:- 如果没有这样的修饰符,它则会以数组形式返回第一个匹配项,索引
0
处保存着完整的匹配项,返回的结果的属性中还有一些其他详细信息:如果正则表达式中有一部分内容被包在括号里,那么返回的数组可能会有0
以外的索引。我们将在 捕获组 中学习这部分相关内容。- 最后,如果没有匹配项,则返回
null
(无论是否有修饰符g
)。
2、str.replace(str|regexp, str|func)
str.replace(regexp, replacement)方法使用
replacement替换在字符串
str中找到的
regexp的匹配项(如果带有修饰符
g` 则替换所有匹配项,否则只替换第一个)。
第二个参数是字符串
replacement
。我们可以在其中使用特殊的字符组合来对匹配项进行插入:
符号 在替换字符串中的行为 $& 插入整个匹配项 $` 插入字符串中匹配项之前的字符串部分 $’ 插入字符串中匹配项之后的字符串部分 $n 如果 n
是一个 1-2 位的数字,则插入第 n 个分组的内容,详见 捕获组$ 插入带有给定 name
的括号内的内容,详见 捕获组$$ 插入字符 $
alert( “I love HTML”.replace(/HTML/, “$& and JavaScript”) ); // I love HTML and JavaScript
3、str.matchAll(regexp)
方法
str.matchAll(regexp)
是str.match
的“更新、改进”的变体。它主要用来搜索所有组的所有匹配项。
与
match
相比有 3 个区别:
- 它返回一个包含匹配项的可迭代对象,而不是数组。我们可以用
Array.from
将其转换为一个常规数组。- 每个匹配项均以一个包含捕获组的数组形式返回(返回格式与不带修饰符
g
的str.match
相同)。- 如果没有结果,则返回的是一个空的可迭代对象而不是
null
。
4、str.split(regexp|substr, limit)
使用正则表达式(或子字符串)作为分隔符来分割字符串。
alert('12, 34, 56'.split(/,\s*/)) // 数组 ['12', '34', '56']
5、str.search(regexp)
方法
str.search(regexp)
返回第一个匹配项的位置,如果没找到,则返回-1
:重要限制:
search
仅查找第一个匹配项。如果我们需要其他匹配项的位置,则应使用其他方法,例如用
str.matchAll(regexp)
查找所有位置。
6、str.replaceAll(str|regexp, str|func)
这个方法与
str.replace
本质上是一样的,但有两个主要的区别:
- 如果第一个参数是一个字符串,它会替换 所有出现的 和第一个参数相同的字符串,而
replace
只会替换 第一个。- 如果第一个参数是一个没有修饰符
g
的正则表达式,则会报错。带有修饰符g
,它的工作方式与replace
相同。
replaceAll
的主要用途是替换所有出现的字符串。