创建正则表达式的方法
/ab+c/i; //字面量形式
new RegExp('ab+c', 'i'); // 首个参数为字符串模式的构造函数
new RegExp(/ab+c/, 'i'); // 首个参数为常规字面量的构造函数
.search和.match方法
.search 如果找到则返回第一个字母的index, 未找到则返回-1
.match 返回于正则匹配的项
let str = "I love mp3"; // 将在这里搜索
let regexp = /love/;
alert( str.search(regexp) ); // 2
alert( str.match(/\s\w\w\d/) ); // mp3
修饰符
- i ——不区分大小写
- g —— 查找所有匹配项
- m ——多行模式
- u ——完整unicode支持
- y ——粘滞模式
- s ——让
.
匹配任何字符
字符类
\d
——数字\D
——非数字\s
——空格,制表符,换行符\S
——除了\s\w
——拉丁字母, 数字,下划线_\W
—— 除了\w.
——除了换行符\n之外的所有字符
匹配所有字符的方法
.
加s
修饰符
/./s
\s
+ \S
/[\s\S]/
unicode
修饰符 u
在正则表达式中提供对 Unicode 的支持。
这意味着两件事:
- 4 个字节长的字符被以正确的方式处理:被看成单个的字符,而不是 2 个 2 字节长的字符。
- Unicode 属性可以被用于查找中
\p{…}
。
有了 unicode 属性我们可以查找给定语言中的词,特殊字符(引用,货币)等等。
let regexp = /\p{Sc}\d/gu;
let str = `Prices: $2, €1, ¥9`;
alert( str.match(regexp) ); // $2,€1,¥9
锚点
插入符号 ^
匹配文本开头,而美元符号 $
- 则匹配文本末尾
^…$用于完全匹配
let goodInput = "12:34";
let badInput = "12:345";
let regexp = /^\d\d:\d\d$/;
alert( regexp.test(goodInput) ); // true
alert( regexp.test(badInput) ); // false
m
修饰符开启多行匹配
let str = `1st place: Winnie
2nd place: Piglet
33rd place: Eeyore`;
alert( str.match(/\w+$/gim) ); // Winnie,Piglet,Eeyore
词边界:\b
有三种不同的位置可作为词边界:
- 在字符串开头,如果第一个字符是单词字符
\w
。 - 在字符串中的两个字符之间,其中一个是单词字符
\w
,另一个不是。 - 在字符串末尾,如果最后一个字符是单词字符
\w
。 ```javascript alert( “Hello, Java!”.match(/\bHello\b/) ); // Hello alert( “Hello, Java!”.match(/\bJava\b/) ); // Java alert( “Hello, Java!”.match(/\bHell\b/) ); // null (no match) alert( “Hello, Java!”.match(/\bJava!\b/) ); // null (no match)
// 也可以匹配数字 alert( “1 23 456 78”.match(/\b\d\d\b/g) ); // 23,78 alert( “12,34,56”.match(/\b\d\d\b/g) ); // 12,34,56
<a name="hMm3Q"></a>
### 转义
- 要在字面(意义)上搜索特殊字符 `[ \ ^ $ . | ? * + ( )`,我们需要在它们前面加上反斜杠 `\`(“转义它们”)。
- 如果我们在 `/.../` 内部(但不在 `new RegExp` 内部),还需要转义 `/`。
- 传递一个字符串(参数)给 `new RegExp` 时,我们需要双倍反斜杠 `\\`,因为字符串引号会消费其中的一个。
```javascript
let regStr = "\\d\\.\\d";
alert(regStr); // \d\.\d (correct now)
let regexp = new RegExp(regStr);
alert( "Chapter 5.1".match(regexp) ); // 5.1
集合和范围[…]
[eao]
意味着查找在 3 个字符 'a'
、'e'
或者 `‘o’ 中的任意一个, 这被叫做一个集合。
请注意尽管在集合中有多个字符,但它们在匹配中只会对应其中的一个。
// 查找 [t 或者 m],然后再匹配 “op”
alert( "Mop top".match(/[tm]op/gi) ); // "Mop", "top"
方括号也可以包含字符范围。
比如说,[a-z]
会匹配从 a
到 z
范围内的字母,[0-5]
表示从 0
到 5
的数字。
alert( "Exception 0xAF".match(/x[0-9A-F][0-9A-F]/g) ); // xAF
除了普通的范围匹配,还有类似 [^…]
的“排除”范围匹配。
它们通过在匹配查询的开头添加插入符号 ^
来表示,它会匹配所有除了给定的字符之外的任意字符。
alert( "alice15@gmail.com".match(/[^\d\sA-Z]/gi) ); // @ and .
在[…]中不需要转义,[-().^+]
会查找 -().^+
的其中任意一个字符。
量词
{n}
\d{5}
表示 5 位的数字,如同 \d\d\d\d\d
\d{3,5}
查找位数为 3 至 5 位的数字\d{3,}
查找位数大于或等于 3 的数字
+
?
*
贪婪模式 vs 懒惰模式
- 贪婪模式:默认情况下,正则表达式引擎会尝试尽可能多地重复量词。例如,
\d+
检测所有可能的字符。当不可能检测更多(没有更多的字符或到达字符串末尾)时,然后它再匹配模式的剩余部分。如果没有匹配,则减少重复的次数(回溯),并再次尝试。 - 懒惰模式通过在量词后添加问号
?
来启用。在每次重复量词之前,引擎会尝试去匹配模式的剩余部分。捕获组
括号将正则表达式的一部分组合在一起,以便量词可以整体应用。
括号组从左到右编号,可以选择用alert( 'Gogogo now!'.match(/(go)+/i) ); // "Gogogo"
(?<name>...)
命名。 ```javascript let dateRegexp = /(?[0-9]{4})-(? [0-9]{2})-(? [0-9]{2})/; let str = “2019-04-30”;
let groups = str.match(dateRegexp).groups;
alert(groups.year); // 2019 alert(groups.month); // 04 alert(groups.day); // 30
可以在结果中获得按组匹配的内容:
- 方法 `str.match` 仅当不带标志 `g` 时返回捕获组。
1. 在索引 `0` 处:完全匹配。
2. 在索引 `1` 处:第一个括号的内容。
3. 在索引 `2` 处:第二个括号的内容。
4. …等等…
```javascript
let str = '<h1>Hello, world!</h1>';
let tag = str.match(/<(.*?)>/);
alert( tag[0] ); // <h1>
alert( tag[1] ); // h1
- 方法
str.matchAll
始终返回捕获组。
matchAll返回的不是数组,而是一个可迭代的伪数组
let results = '<h1> <h2>'.matchAll(/<(.*?)>/gi);
// results - is not an array, but an iterable object
alert(results); // [object RegExp String Iterator]
alert(results[0]); // undefined (*)
results = Array.from(results); // 转换为真正的数组
alert(results[0]); // <h1>,h1 (1st tag)
alert(results[1]); // <h2>,h2 (2nd tag)
如果括号没有名称,则匹配数组按编号提供其内容。命名括号还可使用属性 groups
。
我们还可以使用 str.replace
来替换括号内容中的字符串:使用 $n
或者名称 $<name>
。
let str = "John Bull";
let regexp = /(\w+) (\w+)/;
alert( str.replace(regexp, '$2, $1') ); // Bull, John
可以通过在组的开头添加 ?:
来排除编号组。当我们需要对整个组应用量词,但不希望将其作为结果数组中的单独项时这很有用。我们也不能在替换字符串时引用此类括号。
let str = "Gogogo John!";
// ?: 从捕获组中排除 'go'
let regexp = /(?:go)+ (\w+)/i;
let result = str.match(regexp);
alert( result[0] ); // Gogogo John(完全匹配)
alert( result[1] ); // John
alert( result.length ); // 2(数组中没有更多项)
反向引用:\N 和 \k
按组号反向引用 \N
let str = `He said: "She's the one!".`;
let regexp = /(['"])(.*?)\1/g;
alert( str.match(regexp) ); // "She's the one!"
按命名反向引用 \k
let str = `He said: "She's the one!".`;
let regexp = /(?<quote>['"])(.*?)\k<quote>/g;
alert( str.match(regexp) ); // "She's the one!"
选择(OR)|
我们需要找出编程语言:HTML、PHP、Java 或 JavaScript。
对应的正则表达式为:html|php|java(script)?
。
let reg = /html|php|css|java(script)?/gi;
let str = "First HTML appeared, then CSS, then JavaScript";
alert( str.match(reg) ); // 'HTML', 'CSS', 'JavaScript'
我们通常用圆括号把模式中的选择部分括起来,像这样 before(XXX|YYY)after
前瞻断言和后瞻断言
x(?=y) |
前瞻肯定断言 | x ,仅当后面跟着 y |
---|---|---|
x(?!y) |
前瞻否定断言 | x ,仅当后面不跟 y |
(?<=y)x |
后瞻肯定断言 | x ,仅当跟在 y 后面 |
(?<!y)x |
后瞻否定断言 | x ,仅当不跟在 y 后面 |
粘性标志 “y”,在指定位置处搜索
标记 y
使 regexp.exec
正好在 lastIndex
位置,而不是在它之前,也不是在它之后。
let str = 'let varName = "value"';
let regexp = /\w+/y;
regexp.lastIndex = 3;
alert( regexp.exec(str) ); // null(位置 3 有一个空格,不是单词)
regexp.lastIndex = 4;
alert( regexp.exec(str) ); // varName(在位置 4 的单词)
正则表达式(RegExp)和字符串(String)的方法
str.split(regexp|substr, limit)
使用正则表达式(或子字符串)作为分隔符来分割字符串。
我们可以用 split
来分割字符串,如下所示:
alert('12-34-56'.split('-')) // 数组 ['12', '34', '56']
但同样,我们也可以用正则表达式来做:
alert('12, 34, 56'.split(/,\s*/)) // 数组 ['12', '34', '56']
str.replace(str|regexp, str|func)
这是用于搜索和替换的通用方法,是最有用的方法之一。它是搜索和替换字符串的瑞士军刀。
我们可以不用正则表达式来搜索和替换子字符串:
// 用冒号替换连字符
alert('12-34-56'.replace("-", ":")) // 12:34-56
不过有一个陷阱。
当 replace 的第一个参数是字符串时,它仅替换第一个匹配项。
您可以在上面的示例中看到:只有第一个 “-“ 被 “:” 替换了。
如要找到所有的连字符,我们不应该用字符串 “-“,而应使用带 g 标记的正则表达式 /-/g:
// 将连字符替换为冒号
alert( '12-34-56'.replace( /-/g, ":" ) ) // 12:34:56
regexp.test(str)
方法 regexp.test(str) 查找匹配项,然后返回 true/false 表示是否存在。
let str = "I love JavaScript";
// 这两个测试相同
alert( /love/i.test(str) ); // true
alert( str.search(/love/i) != -1 ); // true