RegExp

文章目录

正则

RegExp 对象表示正则表达式,它是对字符串执行模式匹配的强大工具。

RegExp : Regular(正规) Expression(表达式)

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑

它描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。

正则表达式的特点

  1. 灵活性、逻辑性和功能性非常的强;
  2. 可以迅速地用极简单的方式达到字符串的复杂控制。
  3. 对于刚接触的人来说,比较晦涩难懂。
  4. 就算会写,写的过程没问题,写完再看几乎不认识。

比如:
匹配国内电话号码: /\d{3}-\d{8}|\d{4}-\d{7}/
验证手机号: /^((13[0-9])|(15,[^4,\D])|(18[0,5-9]))\d{8}$/

很难记住

创建方式

  1. 字面量形式创建

/pattern/attributes

  1. 构造函数的方式创建

new RegExp(pattern, attributes);

参数 pattern 是一个字符串,指定了正则表达式的模式或其他正则表达式。

参数 attributes 是一个可选的字符串,包含属性 “g”、”i” 、”m”、”u” 和 “y”,分别用于指定全局匹配、区分大小写的匹配、多行匹配、开启完整的 unicode 支持和粘滞模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 1. 字面量形式创建		/pattern/attributes
var reg = /a/ig

// 2. 构造函数的方式创建 new RegExp(pattern, attributes);
var reg = new RegExp("a","ig");

// 什么时候使用 new RegExp
// 通常我们使用的都是简短语法 /.../。但是它不接受任何变量插入,所以我们必须在写代码的时候就知道确切的 regexp。
var search = prompt("What you want to search?", "love");
var regexp = new RegExp(search);

// 找到用户想要的任何东西
console.log( "I love JavaScript".search(regexp));

使用方式

str.search(reg)

返回找到的匹配项的索引位置,如果没找到则返回 -1。总是查找第一个匹配项。

1
2
3
4
var str = "hello world";
console.log(str.search(/o/)); // 4
console.log(str.search(/o/g)); // 4
console.log(str.search(/a/)); // -1

regexp.test(str)

检验字符串是否有符合正则条件的字符串片段,返回 true/false

基本上和 str.search(reg) != -1 一样,

1
2
3
4
5
var str = "I love JavaScript";

// 这两条语句是一样的
console.log( /love/.test(str) ); // true
console.log( str.search(/love/) != -1 ); // true

str.match(reg )

返回符合正则表达式的字符串片段

  • 不使用 "g" 修饰符的时候,结果是一个数组,里面有该匹配项和额外的属性:

    • index – 匹配项在字符串中所处在的位置,
    • input – 原始字符串。
  • 当使用 "g" 修饰符的时候,str.match 就会返回由所有匹配项组成的数组

1
2
console.log("hello".match(/l/)); // ["l", index: 2, input: "hello", groups: undefined]
console.log("hello".match(/l/g)); // ["l","l"];

regexp.exec(str)

检索字符串中指定的值。返回找到的值,并确定其位置

  • 如果正则没有 g,那么 regexp.exec(str) 返回第一个匹配项,也就是 str.match(reg)
  • 如果正则有 g,那么 regexp.exec(str) 返回第一个匹配项,然后在 regexp.lastIndex记住 该匹配项结束的的位置。下一次调用从 regexp.lastIndex 开始搜索,并且返回下一个匹配项。如果再没有匹配项了,则 regexp.exec 返回 nullregexp.lastIndex 置为 0

reg.lastIndex(可读写) 标示开始下一次匹配的字符位置 跟reg.exec() 协调使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var str = "abababa";
var reg = /ab/g;
console.log(reg.exec(str));
console.log(reg.lastIndex);
console.log(reg.exec(str));
console.log(reg.lastIndex);
console.log(reg.exec(str));
console.log(reg.lastIndex);

// 也可以手动设置 lastIndex
reg.lastIndex = 0;
console.log(reg.lastIndex);
console.log(reg.exec(str));
console.log(reg.lastIndex);

// 正则表达式reg中不写 g 属性,exec只返回匹配到的第一次的子串及下标

y 修饰符

y 修饰符意味着搜索应该并且只能在属性 regexp.lastIndex 指定的位置查找匹配项。

通常搜索是在整个字符串里搜索匹配的子串的。但是当有了 y 修饰符,则只会在 regexp.lastIndex(默认是 0)指定的位置开始查找。

例如:

1
2
3
4
5
6
7
8
9
var str = "I love JavaScript!";
var reg = /javascript/iy;
console.log( reg.lastIndex ); // 0(默认)
console.log( str.match(reg) ); // null, 没有在位置 0 上找到匹配项

reg.lastIndex = 7;
console.log( str.match(reg) ); // JavaScript(搜索正确,该单词确实在位置 7)

// 对于其它 reg.lastIndex,结果都为 null

y 修饰符我们一般只在提高解析器性能的时候使用

str.split(regexp|substr, limit)

使用 regexp(或子字符串)作为分隔符分隔字符串。

我们之前已经使用过 split 的字符串形式,像下面这样:

1
alert('12-34-56'.split('-')) // [12, 34, 56]

但是我们也可以传入一个正则表达式:

1
alert('12-34-56'.split(/-/)) // [12, 34, 56]

str.replace(str|reg, str|func)

最简单的用处 — 搜索和替换子字符串,就像下面这样:

1
2
// 把横线替换成冒号
console.log('12-34-56'.replace("-", ":")) // 12:34-56

replace 的第一个参数是字符串时,它只会查找第一个匹配项。

如果要找到所有的横线,我们不能使用字符串 "-",而是 regexp /-/g,带上 g 修饰符。

1
2
// 用冒号替换所有的横线
console.log( '12-34-56'.replace( /-/g, ":" ) ) // 12:34:56

第二个参数是要替换的字符串。

我们可以在里面使用特殊符号:

符号 插入
$$ "$"
$& 整个匹配项
$` 匹配项前面的字符串部分
$' 匹配项后面的字符串部分
$n 如果 n 是一个 1-2 位的数字,那么这表示从左到右数第 n 个括号的内容

在下面的例子中,使用 $& 将所有的 "John" 替换成 "Mr.John"

1
2
3
4
5
var str = "John Doe, John Smith and John Bull.";

// 对于每个 John — 替换成 Mr.John
console.log(str.replace(/John/g, 'Mr.$&'));
// "Mr.John Doe, Mr.John Smith and Mr.John Bull.";

圆括号通常与 $1$2 一起使用,就像下面的例子:

1
2
3
var str = "John Smith";

console.log(str.replace(/(John) (Smith)/, '$2, $1')) // Smith, John

对于那些需要“智能”替换的场景,第二个参数可以是函数。

它会在每次匹配时被调用,并且其返回值会替换掉匹配项。

例如:

1
2
3
4
5
6
var i = 0;

// 将每个“ho”都替换成函数所返回的结果
console.log("HO-Ho-ho".replace(/ho/gi, function() {
return ++i;
})); // 1-2-3

在上面的示例中,函数每次只返回下一个数字,但通常结果是基于匹配的。

调用该函数 func(str, p1, p2, ..., pn, offset, s) 的参数是:

  1. str — 表示匹配项,即 () 正则表达式中的内容 如 /a([1-9])/ 如果有匹配项,它的值将会是a 加上 1-9 之间的一个数字
  2. p1, p2, ..., pn — 圆括号里的内容(如果有的话),有几个捕获组就有几个这样的参数
  3. offset — 匹配项所在的位置,
  4. s — 源字符串。

如果在 regexp 中没有圆括号,那么该函数总是有 3 个参数:func(str, offset, s)

返回值将会替换第一个参数的位置的内容。

下面的代码展示了匹配的所有信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 显示并且替换所有的匹配项
function replacer(str, offset, s) {
console.log(`Found ${str} at position ${offset} in string ${s}`);
return str.toLowerCase();
}

var result = "HO-Ho-ho".replace(/ho/gi, replacer);
console.log( 'Result: ' + result ); // Result: ho-ho-ho

// shows each match:
// Found HO at position 0 in string HO-Ho-ho
// Found Ho at position 3 in string HO-Ho-ho
// Found ho at position 6 in string HO-Ho-ho

在下面的例子中,有两个圆括号,所以 replacer 有 5 个参数:str 是完全匹配项,然后是圆括号,然后是 offsets

1
2
3
4
5
6
7
8
function replacer(str, name, surname, offset, s) {
// name is the first parentheses, surname is the second one
return surname + ", " + name;
}

let str = "John Smith";

alert(str.replace(/(John) (Smith)/, replacer)) // Smith, John

使用函数赋予了我们终极替换大招,因为这能获取所有的匹配信息,并且能够访问外部变量,可以做任何事情。

组合 []

表达式 介绍
[adgk] 查找给定集合内的任何字符。
[0-9] 查找任何从 0 至 9 的数字。
[a-z] 查找任何从小写 a 到小写 z 的字符。
[A-Z] 查找任何从大写 A 到大写 Z 的字符。
[A-z] 查找任何从大写 A 到小写 z 的字符。

边界

  • ^ 以什么开头 /^a/.test(“ba”)
  • $ 以什么结尾 /a$/.test(“ba”)
  • 注意:^在[]中表示 非 例如:/[^a]/.test(“b”)

量词

  • * 重复零次或更多 >=0

  • + 重复一次或更多次 >=1

  • ? 重复零次或一次 (0 || 1) /[a-z]?/.test(“aa”)

  • {n} n次 (x=n) /[1-9]{5}/.test(“1234”)

  • {n,} 重复n次或更多 (x>=n) “a123b4567”.match(/[0-9]{2,}/g)

  • {n,m} 重复出现的次数比n多但比m少 (n<=x<=m) /[0-9]{3,5}/.test(“a123bcd”)

  • x|y x或者y

  • /a{1,3}?/ 能取1个不取三个 ?加在量词之后表示打破贪婪匹配,能取少,不取多,就是不贪婪匹配

    1
    2
    3
    4
    5
    6
    7
    8
    "aaaabbbaaaaaaaaabbaaaa".match(/a{3,5}/g);
    // log: ["aaaa", "aaaaa", "aaaa", "aaaa"]
    "aaaaaaaaaaaaaaaaa".match(/a+/g);
    // log: ["aaaaaaaaaaaaaaaaa"]
    "aaaaaaaaaaaaaaaaa".match(/a+?/g);
    // log: ["a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a"]
    "aaaabbbaaaaaaaaabbaaaa".match(/a{3,5}?/g);
    // log:  ["aaa", "aaa", "aaa", "aaa", "aaa"]

捕获组 ()

正则模式的一部分可以用括号括起来 (...),由此构成一个『捕获组』。每次成功捕获后,RegExp 对象中,会保存最近一次正则的捕获组,如 RegExp.$1 表示最近一次正则匹配的第一个捕获组。

注意:如果加上 g 修饰符, 正则的捕获组将不会被放到结果数组中, 只会出现完整的匹配结果

这有两个作用:

  1. 当使用 String.matchRegExp.exec 方法时,它允许你把匹配到的部分放到一个独立的数组项里面。
  2. 如果我们在括号之后加上量词,那么它会应用到这个整体,而非最后一个字符。

例如:

1
2
var str = "Hahaha welcome";
console.log(str.match(/(ha)+/i)); // ["Hahaha", "ha"]

如果没有括号,模式 /ha+/ 则表示 h 之后跟上一个或多个 a。比如: haaaaa,捕获括号将 ha 划为了一组。

捕获结果的首项是完整的匹配结果,之后就是各个捕获组,从左往右依次排开。如果某个捕获组是可选的,且在匹配中没有找到对应的项,那么在相应的匹配结果中,该项依然会存在(值为 undefined)。

例如:

1
2
3
4
5
var str = "a";
console.log(str.match(/a(b)?(c)?/)); // ["a", undefined, undefined]

var str1 = "ac";
console.log(str.match(/a(b)?(c)?/)); // ["ac", undefined, "c"]

非捕获组 ?:

某些时候,我们会希望使用括号来正确设置量词,但是并不希望其内容出现在结果数组中。可以通过在开头加上 ?: 从而在结果中排除该组。

例如,我们希望找到 (ha)+,但是并不希望其内容(ha)出现在单独的数组项中,那么我们可以这样写:(?:ha)+

1
2
3
4
5
6
var str = "Hahaha interesting";
var reg = /(?:ha)+/i;
var reg1 = /(?:ha)+ (\w+)/i;

console.log(str.match(reg)); // ["Hahaha"]
console.log(str.match(reg1)) // ["Hahaha interesting", "interesting"]

元字符

名称 含义
. 查找单个字符,除了换行和行结束符。
\w 查找单词字符。英语字母表中的一个字母或者一个数字或一个下划线
\W 查找非单词字符。
\d 查找数字。
\D 查找非数字字符。
\s 查找空白字符。
\S 查找非空白字符。
\b 匹配单词边界。
\B 匹配非单词边界。

注意:\b 和 \B 是根据方向存在的

环视断言

当我们想根据前面/后面的上下文筛选出一些东西的时候,前瞻断言和后瞻断言(通常被称为“环视断言”)对于简单的正则表达式就很有用。

环视断言类型:

模式 类型 匹配
x(?=y) 前瞻肯定断言 匹配 x ,仅当后面跟着 y
x(?!y) 前瞻否定断言 匹配 x ,仅当后面不跟 y
(?<=y)x 后瞻肯定断言 匹配 x ,仅当跟在 y 后面
(?<!y)x 后瞻否定断言 匹配 x ,仅当不跟在 y 后面

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var str = "2 servings of rice are worth 30¥";

// 如果我们要选择后面带¥的数字
var reg = /\d+(?=¥)/;
// 如果我们选择后面不带¥的数字,由于后面带¥的字符串后边界必定是¥,用\b排除了就行,否者可能会打出["2","3"],下面带\b的同理
var reg = /\d+(?!¥)\b/g;

var str = "1 duck costs ¥28";

// 如果我们要选择前面带¥的数字
var reg = /(?<=¥)\d+/;
// 如果我们选择前面不带¥的数字
var reg = /\b(?<!¥)\d+/;

console.log(str.match(reg));

正则查看器

正则查看器

分享到:

评论完整模式加载中...如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理