正则表达式是提供了一种在文本中进行搜索和替换的强大的方式的模式。
本文主要介绍JavaScript中的正则表达式。
创建一个正则表达式
正则表达式(可叫作 “regexp”,或 “reg”)由 模式 和可选的 修饰符 组成。
有两种创建正则表达式对象的语法。
1.调用RegExp对象的构造函数:
regexp = new RegExp("pattern", "flags");
2.使用一个正则表达式字面量,其由包含在斜杠之间的模式组成:
regexp = /pattern/; // 没有修饰符
regexp = /pattern/gmi; // 带有修饰符 g、m 和 i
在这两种情况下,regexp 都会成为内建类 RegExp 的一个实例。
这两种语法之间的主要区别在于,使用斜线 /.../ 的模式不允许插入表达式(如带有 ${...} 的字符串模板)。它是完全静态的。
修饰符
i:使用此修饰符后,搜索时不区分大小写:A 和 a 之间没有区别(请参见下面的示例)。
g:使用此修饰符后,搜索时会寻找所有的匹配项 —— 没有它,则仅返回第一个匹配项。
m:多行模式。
s:启用 “dotall” 模式,允许点 . 匹配换行符 \n。
u:开启完整的 Unicode 支持。该修饰符能够**正确处理代理对。**比如说遇到表情符号。
y:粘滞(Sticky)模式,在文本中的确切位置搜索。
元字符
\d:匹配任何数字,等价于 [0-9]。
\D:匹配任何非数字字符。
\w:匹配任何字母、数字或下划线,等价于 [a-zA-Z0-9_]。
\W:匹配任何非字母、数字或下划线。
\s:匹配任何空白字符(空格、制表符等)。
\S:匹配任何非空白字符。
.:匹配除换行符外的任意字符。带有修饰符 's' 时匹配任何字符。
锚点
^ :匹配文本开头。
- 例如,
/^abc/会匹配以abc开头的字符串。
$ :则匹配文本末尾。
- 例如,
/abc$/会匹配以abc结尾的字符串。
可以使用修饰符m开启多行模式。默认情况下^只匹配文本的开头,多行模式下除了匹配文本的开头,也匹配所有以换行符\n开头的位置。多行模式下 $ 匹配所有以换行符 \n 结尾的位置和文本末尾位置(文本末尾是没有换行符\n的)。
词边界
\b:匹配单词边界(例如字母与非字母之间的界限)。
- 例如,
\bword\b会匹配单词word,但不会匹配sword或wording。
\B:匹配非单词边界。
- 例如,
\Bword\B不会匹配word,但会匹配sword中的word。
转义
\:转义字符。
- 要在字面意义上搜索特殊字符
[ \ ^ $ . | ? * + ( ),我们需要在它们前面加上一个反斜杠\(“转义它们”)。 - 如果在
/.../内(但不在new RegExp内),我们还需要转义/。 - 当将字符串传递给给
new RegExp时,我们需要双反斜杠\\,因为字符串引号会消耗一个反斜杠。
集合
[abc]: 匹配 a、b 或 c 中的任意一个。
范围
[a-z] :表示从 a 到 z 范围内的字符,[0-5] 表示从 0 到 5 的数字。
排除范围
[^…] :“排除”范围匹配。
[^aeyo]—— 匹配除了'a'、'e'、'y'或'o'之外的任何字符。[^0-9]—— 匹配除了数字之外的任何字符,与\D作用相同。
通过在开头添加插入符号 ^ 来表示匹配所有 除了给定的字符 之外的任意字符。
Tips:
在方括号,我们可以使用绝大多数特殊字符而无需转义:
- 符号
. + ( )无需转义。 - 在开头或结尾(未定义范围)的连字符
-不会被转义。 - 插入符号
^仅在开头会被转义(表示排除)。 - 右方括号
]总是会被转义(如果我们需要寻找那个符号)。
量词
在一个字符(或一个字符类,或 [...] 等)后附加一个量词,用来指出我们具体需要的数量。
*:匹配前面的元素零次或多次。
+:匹配前面的元素一次或多次。
?:匹配前面的元素零次或一次。
{n}:匹配前面的元素恰好 n 次。
{n,}:匹配前面的元素至少 n 次。
{n,m}:匹配前面的元素至少 n 次,但不超过 m 次。
贪婪量词和惰性量词
贪婪量词
当然,量词也有棘手的地方,举个例子,想要定位带引号的字符串,结果是这样的:
let regexp = /".+"/g;
let str = 'a "witch" and her "broom" is one';
alert( str.match(regexp) ); // "witch" and her "broom"
它没有找到匹配项 "witch" 和 "broom",而是找到:"witch" and her "broom"。
因为在贪婪模式下(默认情况),量词都会尽可能多地重复。
惰性量词
惰性模式中的量词与贪婪模式中的是相反的。它表示:“重复最少的次数”。
我们可以通过在量词后面添加一个问号 '?' 来启用它。
通常问号 ? 本身就是一个量词(0 或 1),但如果将其放到 另一个量词(甚至是它自己)后面,就会有不同的含义 —— 它将匹配的模式从贪婪转为惰性。
例如,/".+?"/g就可以使代码正常工作啦。
let regexp = /".+?"/g;
let str = 'a "witch" and her "broom" is one';
alert( str.match(regexp) ); // "witch", "broom"
捕获组
模式的一部分可以用括号括起来 (...)。这被称为“捕获组(capturing group)”。
这有两个影响:
- 它允许将匹配的一部分作为结果数组中的单独项。
- 如果我们将量词放在括号后,则它将括号视为一个整体。
在匹配括号中的内容时,括号被从左到右编号。正则引擎会记住它们各自匹配的内容,并允许在结果中获取它。
方法 str.match(regexp),如果 regexp 没有修饰符 g,将查找第一个匹配项,并将它作为数组返回:
- 在索引
0处:完整的匹配项。 - 在索引
1处:第一个括号的内容。 - 在索引
2处:第二个括号的内容。 - ……
例如
let str = '<span class="my">';
let regexp = /<(([a-z]+)\s*([^>]*))>/;
let result = str.match(regexp);
alert(result[0]); // <span class="my">
alert(result[1]); // span class="my"
alert(result[2]); // span
alert(result[3]); // class="my"
即使组是可选的并且在匹配项中不存在(例如,具有量词 (...)?),也存在相应的 result 数组项,并且等于 undefined。
例如
let match = 'a'.match(/a(z)?(c)?/); //z和c都是可选项
alert( match.length ); // 3
alert( match[0] ); // a(完整的匹配项)
alert( match[1] ); // undefined
alert( match[2] ); // undefined
如果想实现替换,那么使用$n可以替换 str 中 regexp 的所有匹配项的方法 str.replace(regexp, replacement) 允许我们在 replacement 字符串中使用括号中的内容。(其中 n 是组号)
例如
let str = "John Bull";
let regexp = /(\w+) (\w+)/;
alert( str.replace(regexp, '$2, $1') ); // Bull, John
常用方法
regxp.test():测试字符串是否匹配正则表达式。然后返回 true/false 表示是否存在。
const regex = /abc/;
console.log(regex.test('abcdef')); // true
regxp.exec():在字符串中执行查找匹配,并返回匹配结果。
const regex = /abc/;
const result = regex.exec('abcdef');
console.log(result); // ["abc"]
str.match():返回与正则表达式匹配的结果。
const regex = /abc/;
const str = 'abcdef';
console.log(str.match(regex)); // ["abc"]
str.replace():用新字符串替换与正则表达式匹配的部分。
const regex = /abc/;
const newStr = 'abcdef'.replace(/abc/, 'xyz');
console.log(newStr); // "xyzdef"
参考文献:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_expressions
