ECMAScript正则表达式

我只是文档搬运工,总结得不好,难免疏漏,若有错误请帮我指出,谢谢。

1. 如何创建

  • 使用RegExp构造函数
  • 使用正则表达式字面量

使用字面量定义的任何正则表达式都可以使用构造函数来构建

正则表达式是构成搜索模式(search pattern)的字符序列,匹配字符串中字符组合的模式。相当于是一个描述或者一个检索规则,针对你想要操作的内容。

在JS中 RegExp 是对象,有 test 方法和 exec 方法,同时还有字符串的 match、replace、search、split、matchAll 方法可用。可通过 正则表达式字面量 直接创建或者 new RegExp 对象。

mdn中说道:脚本加载后,正则表达式字面量就会被编译。当正则表达式保持不变时,使用字面量可获得更好的性能。在脚本运行的过程中,用构造函数创建的正则表达式会被编译。若正则表达式发生该改变,或者需要从用户输入等来源中动态的产生,就需要使用构造函数来创建正则表达式。在需要改变正则表达式是可以使用compile重新编译。

为什么要重新编译呢?我发现测试时即便不重新编译输出结果也是一样的,我在掘金找到一份回答:https://juejin.im/post/5b6adc... 也许有用 大意为:如果指定的正则表达式需要多次重复使用,那么编译正则表达式将会提高代码的执行效率,不过如果仅仅执行一次或者少数几次,那么将不会有明显的效果,compile提高了正则表达式的适应性!

2. 如何编写

格式:/pattern/flags;

2.1. 确定字符串

直接使用 /str/即可

2.2. 不确定字符串

2.2.1. 断言

2.2.1.1. Assertions

表示一个匹配在某些条件下发生。断言包含先行断言、后行断言和条件表达式。

    const text = 'A quick fox';
    const regexpLastWord = /\w+$/;
    console.log(text.match(regexpLastWord));
    // expected output: Array ["fox"]
    const regexpWords = /\b\w+\b/g; // \b表单词边界
    console.log(text.match(regexpWords));
    // expected output: Array ["A", "quick", "fox"]
    const regexpFoxQuality = /\w+(?= fox)/; // 匹配前面的\w+仅当\w+后面跟着fox时,这种叫做先行断言。
    console.log(text.match(regexpFoxQuality));
    // expected output: Array ["quick"]
Characters Meaning
^ Matches the beginning of input
$ Matches the end of input
\b Matches a word boundary
\B Matches a non-word boundary
x(?=y) Matches "x" only if "x" is followed by "y"
x(?!y) Matches "x" only if "x" is not followed by "y"
(?<=y)x Matches "x" only if "x" is preceded by "y"
(? Matches "x" only if "x" is not preceded by "y"
attention:(different meaning in the start of group)

2.2.1.2. 附加:Groups and ranges

Characters Meaning
`x y` Matches either "x" or "y"
[xyz] [a-c] A character set.
[^xyz] [^a-c] A negated or complemented character set
(x) Matches x and remembers the match
\n Where "n" is a positive integer
(?x) Named capturing group
(?:x) Non-capturing group
attention: (?x) Matches "x" and stores it on the groups property of the returned matches under the name specified by

2.2.2. 边界(Boundaries)

表示行和单词的开始和结尾。

2.2.3. 字符类别(Character Classes)

区分不同类型的字符,例如区分字母和数字。

2.2.4. 组和范围(Groups and Ranges)

表示表达式字符的分组和范围。

2.2.5. 量词(Quantifiers)

表示匹配的字符或表达式的数量。

2.2.6. Unicode 属性转义(Unicode Property Escapes)

基于 unicode 字符属性区分字符。例如大写和小写字母、数学符号和标点。

2.3. 特殊字符

我看了 w3school.com.cn 的解释和 MDN 的解释,对比一下,建议看 MDN 正则表达式中特殊字符的含义

量词 描述
n+ 匹配前面的模式 n 1 次或多次
n* 匹配前面的模式 n 0 或多次
n? 匹配前面的模式 n 0 或 1 次
n{X} x 是一个正整数。前面的模式 n 连续出现 x 次时匹配
n{X,Y} 匹配包含 X 至 Y 个 n 的序列的字符串。
n{X,} 匹配包含至少 X 个 n 的序列的字符串。
n$ 匹配任何结尾为 n 的字符串。
^n 匹配任何开头为 n 的字符串。
?=n 匹配任何其后紧接指定字符串 n 的字符串。
?!n 匹配任何其后没有紧接指定字符串 n 的字符串。
`x\ y` 匹配 x 或 y

2.4. 修饰符

2.4.1. syntax

var re = /pattern/flags; or var re = new RegExp('pattern', 'flags');

flag 意义 对应的属性
i 不区分大小写 RegExp.prototype.global
g 全局搜索 RegExp.prototype.ignoreCase
m 多行搜索 RegExp.prototype.multiline
s 允许 . 匹配换行符 (在 ES2018 添加,Firefox不支持) RegExp.prototype.dotAll
u 使用unicode码的模式进行匹配 RegExp.prototype.unicod
y 执行 粘性 搜索,匹配从目标字符串的当前位置开始,可以使用y标志 RegExp.prototype.sticky

2.4.2. 附加

  1. 标志是一个正则表达式的一部分,在正则表达式创建之后,它们在接下来的时间将不能添加或删除。
  2. 使用.exec()方法时,与'g'标志关联的行为是不同的。在一开始讲 创建 的时候就提过exec是RegExp的方法,而match是String的方法,这时候来看为什么两个方法在没有g标志时看似是一样的,为什么有了g就不一样呢?

exec()的情况下,正则表达式是 拥有该方法 的正则表达式,其中字符串是参数;在.match()的情况下,字符串类(or data type)拥有该方法,而正则表达式只是一个参数。对比str.match(reg)reg.exec(str) , 'g'标志与exec()方法一起使用因为lastIndex参数会迭代进行,这在后面讲exec方法有详细介绍。

  1. 其中的m代表的多行匹配如何使用?

m标志用于指定多行输入字符串应该被视为多个行。如果使用m标志,^和$匹配的开始或结束输入字符串中的每一行,而不是整个字符串的开始或结束。

    var reg = new RegExp("^.", "gm");
    var str = "hui \nhello \nfuck!";
    console.log(str.match(reg)); //(3) ["h", "h", "f"]

2.4.3. 例子

    var names = "Orange Trump ;Fred Barney; Helen Rigby ; Bill Abel ; Chris Hand ";
    var output = ["---------- Original String\n", names + "\n"];
    // 此时output = ["---------- Original String↵", "Orange Trump ;Fred Barney; Helen Rigby ; Bill Abel ; Chris Hand ↵"]
    var pattern = /\s*;\s*/;
    var nameList = names.split(pattern);
    // 此时nameList = ["Orange Trump", "Fred Barney", "Helen Rigby", "Bill Abel", "Chris Hand "]
    pattern = /(\w+)\s+(\w+)/;
    var bySurnameList = [];
    output.push("---------- After Split by Regular Expression");
    // 此时output = ["---------- Original String↵", "Orange Trump ;Fred Barney; Helen Rigby ; Bill Abel ; Chris Hand ↵", "---------- After Split by Regular Expression"]
    var i, len;
    for (i = 0, len = nameList.length; i < len; i++) {
        output.push(nameList[i]);
        bySurnameList[i] = nameList[i].replace(pattern, "$2, $1");
        console.log("xixixi:" + bySurnameList[i]);
    }

    // 输出新的数组
    output.push("---------- Names Reversed");
    for (i = 0, len = bySurnameList.length; i < len; i++) {
        output.push(bySurnameList[i]);
    }

    // 根据姓来排序,然后输出排序后的数组。
    bySurnameList.sort();
    output.push("---------- Sorted");
    for (i = 0, len = bySurnameList.length; i < len; i++) {
        output.push(bySurnameList[i]);
    }

    output.push("---------- End");
 
    console.log(output.join("\n"));

2.5. RegExp 对象方法

2.5.1. RegExpObject.compile(regexp,modifier)

用于在脚本执行过程中编译正则表达式,也可用于改变或重新编译正则表达式。modifier只有i、g

    var str = "Every man in the world! Every woman on earth!";
    console.log(str);
    patt = /man/g;
    str2 = str.replace(patt, "person");
    console.log(str2);
    console.log(patt);

    patt = /(wo)?man/g;
    console.log(patt);

    patt.compile(patt); //改变正则表达式之后重新编译
    // 为什么要重新编译呢?我发现测试时即便不重新编译输出结果也是一样的
    // 我在掘金找到一份回答:https://juejin.im/post/5b6adc7ee51d4534b8587560#heading-22
    // 大意为:如果指定的正则表达式需要多次重复使用,那么编译正则表达式将会提高代码的执行效率,不过如果仅仅执行一次或者少数几次,那么将不会有明显的效果,compile提高了正则表达式的适应性!
    console.log(patt);

    str2 = str.replace(patt, "person");
    console.log(str2);

2.5.2. RegExpObject.exec(string)

通过模式搜索字符串并返回已找到的文本,返回的是一个数组,但即使字符串中有多个匹配的文本时也只会返回第一个匹配到的结果。

exec更加灵活也更加复杂,如果 exec() 找到了匹配的文本,则返回一个结果数组。否则,返回 null。此数组的第 0 个元素是与正则表达式相匹配的文本,第 1 个元素是与 RegExpObject 的第 1 个子表达式相匹配的文本(如果有的话),第 2 个元素是与 RegExpObject 的第 2 个子表达式相匹配的文本(如果有的话),以此类推。除了数组元素和 length 属性之外,exec() 方法还返回两个属性。index 属性声明的是匹配文本的第一个字符的位置。input 属性则存放的是被检索的字符串 string。我们可以看得出,在调用非全局的 RegExp 对象的 exec() 方法时,返回的数组与调用方法 String.match() 返回的数组是相同的。

但是,当 RegExpObject 是一个全局正则表达式时,exec() 的行为就稍微复杂一些。它会在 RegExpObject 的 lastIndex 属性指定的字符处开始检索字符串 string。当 exec() 找到了与表达式相匹配的文本时,在匹配后,它将把 RegExpObject 的 lastIndex 属性设置成匹配到的文本中的最后一个字符的下一个位置,在下面的例子中也就是每次匹配到的hui中的i字符对应的下一个位置。可知通过反复调用 exec() 方法可遍历字符串中的所有匹配文本。当 exec() 再也找不到匹配的文本时,它将返回 null,并把 lastIndex 属性重置为 0。

    var reg = new RegExp(/hui/ig);
    var str = 'hui hui hui Like Summer';
    console.log(reg.lastIndex); //0
    console.log(reg.exec(str));//["hui", index: 0, input: "hui hui hui Like Summer", groups: undefined]
    // 匹配的的文本的最后一个字符的下一位即2的下一位为3
    console.log(reg.lastIndex); //3
    console.log(reg.exec(str));//["hui", index: 4, input: "hui hui hui Like Summer", groups: undefined]
    console.log(reg.lastIndex); //7
    console.log(reg.exec(str));//["hui", index: 8, input: "hui hui hui Like Summer", groups: undefined]
    console.log(reg.lastIndex); //11
    console.log(reg.exec(str));//null
    console.log(reg.lastIndex);//0
    var reg = /\w+\s/g;
    var str = "fee fi fo fum";
    var xArray;
    while (xArray = reg.exec(str)) console.log(xArray);
    // ["fee ", index: 0, input: "fee fi fo fum"]
    // ["fi ", index: 4, input: "fee fi fo fum"]
    // ["fo ", index: 7, input: "fee fi fo fum"]

如果在reg匹配过之后要换一个字符串重新进行匹配,那么需要手动把reg.lastIndex设置为0,否则回像下面这样明明存在却匹配不出来。

    var reg = new RegExp(/hui/ig);
    var str = 'hui hui hui Like Summer';
    console.log("lastIndex:" + reg.lastIndex); //0
    console.log(reg.exec(str));
    console.log("lastIndex:" + reg.lastIndex); //3
    console.log(reg.exec('hui Like Summer'));

同时exec相比于match返回的信息要详细的多。

    var reg = new RegExp(/hui/ig);
    var str = '  HuiDT hui hui hui Like Summer';
    console.log(reg.exec('  HuiDT hui hui hui Like Summer'));
    console.log(reg.exec(str));
    console.log(/hui/ig.exec('  HuiDT hui hui hui Like Summer'));
    console.log(/hui/ig.exec(str));

2.5.3. test()

看字符串中是否存在正则表达式(通过模式来搜索字符串),返回 true 或 false。

    var reg = /hui/;
    var str = '  huiDT Like Summer';
    console.log(reg.test('huiDT Like Summer'));
    console.log(reg.test(str));
    console.log(/hui/.test('huiDT Like Summer'));
    console.log(/hui/.test(str));

2.6. RegExp实例属性

RegExp的每个实例都具有以下属性,通过这些属性可以得到一些信息。

  • global:布尔值,表示是否设置了g标志。
  • ignoreCase:布尔值,表示是否设置了i标志。
  • multiline:整数,表示开始搜索写一个匹配项的字符位置,从0算起。
  • lastIndex:布尔值,表示是否设置了m标志。
  • source:正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回。

2.7. RegExp构造函数属性

RegExp构造函数包含一些属性,适用于作用域中的所有正则表达式,这些属性分别有一个长属性和短属性名。

长属性名 短属性名 说明
input $_ 最近一次要匹配的字符串
lastMatch $& 最近一次的匹配项
lastParen $+ 最近一次匹配的捕获组
leftContext $` input字符串中lastMatch之前的文本
multiline $* 布尔值,表示是否所有表达式都是用多行模式
rightContext $' Input字符串中lastMatch之后的文本

2.8. 支持正则表达式的 String 对象的方法

  1. seaech():接受字符串作为搜索参数。字符串参数将被转换为正则表达式,返回结果是模式第一次出现的位置。
    var reg = /hui/i;
    var str = 'DT hui Like Summer';
    console.log(str.search(reg)); //3
    console.log(str.search("hui")); //3
    console.log(str.search(/hui/i)); //3
    console.log('DT hui Like Summer'.search(reg)); //3
    console.log('DT hui Like Summer'.search("hui")); //3
    console.log('DT hui Like Summer'.search(/hui/i)); //3
  1. replace:替换与正则表达式匹配的子串。
    var reg = /hui/i;
    var str = 'DT hui Like Summer';
    var str1 = '会';
    console.log(str.replace('DT', '会'));
    console.log(str.replace(/hui/, '会'));
    console.log(str.replace(/DT/, str1));
    console.log(str.replace(reg, str1));
    console.log('DT  DT hui Like Summer'.replace(/DT/g, '会'));
  1. match:找到一个或多个正则表达式的匹配。
    var reg = new RegExp("hui", 'ig');
    var str = "Huihui Like Summer";
    console.log(str.match(reg));

正则表达式由普通字符和特殊字符(元字符)组成,其中普通字符包括非打印字符

  1. split:把字符串分割为字符串数组。

2.9. 方括号

表示范围,用来查找指定范围内的字符

  1. [abc]找方括号中出现的字符,即给定集合中出现的字符
    var reg = /[abc]/ig;
    var str = "A a b d hui";
    console.log(str.match(reg)); //["A", "a", "b"]
  1. 1找方括号中没有出现的所有字符,即给定集合之外的字符
    var reg = /[^abc]/ig;
    var str = "A a b d hui";
    console.log(str.match(reg)); // [" ", " ", " ", "d", " ", "h", "u", "i"]
  1. [0-9]:查找任何从 0 至 9 的数字。
  2. [a-z]:查找任何从小写 a 到小写 z 的字符。
  3. [A-Z]:查找任何从大写 A 到大写 Z 的字符。
  4. [A-z]:查找任何从大写 A 到小写 z 的字符。
  5. (red|blue|green):查找任何指定的选项。
    var str = "abchui";
    console.log(str.match(/[|a|b|c]/ig)); //["a", "b", "c"]
    console.log(str.match(/[^|a|b|c]/ig)); //["h", "u", "i"]
    console.log(str.match(/(^a|b|c)/ig)); //["a", "b", "c"]
    console.log(str.match(/(a|hui)/ig)); //["a", "hui"]

2.10. 元字符

元字符指拥有特殊含义的字符

在试图匹配它们时要求转义。

特殊字符 描述
. 查找单个字符,除了换行和行结束符。
\w 查找单词字符(除符号、空格)
\W 查找非单词字符。
\d 查找数字。
\D 查找非数字字符。
\s 查找空白字符。
\S 查找非空白字符。
\b 匹配单词边界。
\B 匹配非单词边界。
\0 查找 NUL 字符。
\n 查找换行符。
\f 查找换页符。
\r 查找回车符。
\t 查找制表符。
\v 查找垂直制表符。
\xxx 查找以八进制数 xxx 规定的字符。
\xdd 查找以十六进制数 dd 规定的字符。
\uxxxx 查找以十六进制数 xxxx 规定的 Unicode 字符。

【例】

    var str = "Like's 520";
    var reg1 = /./ig;
    console.log(str.match(reg1)); // ["L", "i", "k", "e", "'", "s", " ", "5", "2", "0"]
    var reg2 = /\w/ig;
    console.log(str.match(reg2)); //["L", "i", "k", "e", "s", "5", "2", "0"]
    var reg3 = /\W/ig;
    console.log(str.match(reg3)); //["'", " "]
    var reg4 = /\d/ig;
    console.log(str.match(reg4)); //["5", "2", "0"]
    var reg5 = /\D/ig;
    console.log(str.match(reg5)); //["L", "i", "k", "e", "'", "s", " "]
    var reg6 = /\s/ig;
    console.log(str.match(reg6)); //[" "]
    var reg7 = /\S/ig;
    console.log(str.match(reg7)); //["L", "i", "k", "e", "'", "s", "5", "2", "0"]
    var reg8 = /\b/ig; //单词边界,找单词旁边的空格
    console.log(str.match(reg8)); //(6) ["", "", "", "", "", ""]

    var reg9 = /\B/ig; //非单词边界,找连续单词之间的间隔有几个
    console.log(str.match(reg9)); //(5) ["", "", "", "", ""]
    console.log("1234567".match(reg9)); //(6) ["", "", "", "", "", ""]

    str = "Like's hui A ቐ W \0 \n \f \r \t \v  0b10 070 0xaa ";
    var reg10 = /\0/ig;
    console.log(str.match(reg10));
    var reg11 = /\n/ig;
    console.log(str.match(reg11));
    var reg12 = /\f/ig;
    console.log(str.match(reg12));
    var reg13 = /\r/ig;
    console.log(str.match(reg13));
    var reg14 = /\t/ig;
    console.log(str.match(reg14));
    var reg15 = /\v/ig;
    console.log(str.match(reg15)); //[""]
    var reg16 = /\150/g; // 找八进制数
    console.log(str.match(reg16));
    var reg17 = /\151/g; //找八进制数
    console.log(str.match(reg17));
    console.log(str.match(/\x57/g)); //找十六进制数
    var reg18 = /\u0041/g; //查找以十六进制数 xxxx 规定的 Unicode 字符
    console.log(str.match(reg18));
    console.log(str.match(/\u1250/));
   ( [  { \ ^ $ | ) ? * + . ] }

用法参考:https://developer.mozilla.org...

元字符 描述
$ 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身,请使用 \$
( ) 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \(\)
* 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*
+ 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+
. 匹配除换行符 \n之外的任何单字符。要匹配 .,请使用 \
[ 标记一个中括号表达式的开始。要匹配 [,请使用 \[
? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用
\ 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\\' 匹配 "\",而 '\(' 则匹配 "("。
^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^
{ 标记限定符表达式的开始。要匹配 {,请使用 \{
`\ ` 指明两项之间的一个选择。要匹配 `\ ,请使用 `\

2.11. 非打印字符

字符 描述
\cx 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。
\f 匹配一个换页符。等价于 \x0c\cL
\n 匹配一个换行符。等价于 \x0a\cJ
\r 匹配一个回车符。等价于 \x0d\cM
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]
\S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]
\t 匹配一个制表符。等价于 \x09\cI
\v 匹配一个垂直制表符。等价于 \x0b\cK

2.12. 运算符优先级

2.13. 匹配规则

    var str = "hui hello    fuck! Orange Trump";
    str1 = str.replace(/(\w+)\s+(\w+)/g, "$2,$1");
    console.log(str1); //hello,hui    fuck! Trump,Orange

3. 局限性

ECMAScript缺少某些语言所支持的该机正则表达式特性(特别是perl)。


  1. abc

你可能感兴趣的:(javascript,正则表达式,ecmascript)