JavaScript中的正则表达式
部分尖括号
<>内容表示需要手动输入值
结构
有两种方法创建一个 RegExp 对象:
-
字面量:
//gim -
构造函数:
new RegExp("", "gim")
一般推荐使用字面量创建正则表达式;在需要使用变量或为了方便阅读使用字符串拼接时,可使用构造函数形式。
RegExp 能设置三个标识,分别由字母g、i、m表示:
| 标识 | 含义 |
|---|---|
| g | 全局的(匹配多次;不同的方法对g标识的处理不尽相同) |
| i | 大小写不敏感 |
| m | 多行(^和$能匹配行尾符) |
RegExp 对象的属性:
| 属性 | 用法 |
|---|---|
| global | 如果标识g被使用,值为true |
| ignoreCase | 如果标识i被使用,值为true |
| lastIndex | 下一次exec匹配开始的索引。初始值为0 |
| multiline | 如果标识m被使用,值为true |
| source | 正则表达式源码文本 |
元素
分支
一个正则表达式分支包含一个或多个正则表达式序列。
这些序列被|字符分割。如果这些序列中任何一项符合匹配条件,那么这个选择就被匹配。它尝试按顺序依次匹配这些序列项。故:
"into".match(/in|int/);
会匹配in。它不会匹配int,因为in已经被成功匹配了。
序列
一个正则表达式序列包含一个或多个正则表达式因子。
每个因子可选择是否跟随一个量词,这个量词决定这个因子被允许匹配的次数。如果没有指定量词,则该因子只会被匹配一次。
因子
一个正则表达式因子可以是一个字符、一个由圆括号包围的组、一个字符类、或者一个转义序列。
除了控制字符和特殊字符以外,所有的字符都会被按照字面处理:
\ / [ ] ( ) { } ? + * | . ^ $
如果想让以上字符按字面去匹配,必须要用一个\前缀来进行转义。
一个未被转义的.会匹配除行结束符以外的任何字符。
当指定了g标识且lastIndex属性值为0或未指定g标识时,一个未转义的^会匹配文本的开始;当指定了m标识时,它也能匹配行结束符。
一个未转义的$将匹配文本的结束。当指定了m标识时,它也能匹配行结束符。
转义
反斜杠字符\在正则表达式因子中与其在字符串中一样均表示转义。
转义字符含义:
| 转义字符 | 含义 |
|---|---|
| \f | 换页符 |
| \n | 换行符 |
| \r | 回车符 |
| \t | 制表符(tab) |
| \u | 允许指定一个 Unicode 字符来表示一个十六进制的常量 |
| \d | 等同于[0-9],它匹配一个数字。\D则表示与其相反的[^0-9] |
| \s | 等同于[\f\n\r\t\u000B\u0020\u00A0\u2028\u2029]。这是 Unicode 空白符的一个不完全子集。\S则与其相反 |
| \w | 等同于[0-9A-Za-z_]。\W则与其相反。\W本意是希望表示出现在话语中的字符。遗憾的是,它所定义的类实际上对任何真正的语言来说都不起作用 |
| \b | 被指定为一个字边界标识,用于对文本的字边界进行匹配。遗憾的是,它使用\w去寻找边界,所以他对多语言应用来说是完全无用的 |
| \1 | 指向分组 1所捕获到的文本的一个引用,所以它能再次匹配。如正则表达式:d;\2是指向分组 2的引用,以此类推 |
如正则表达式:
/(?:^|\s+?)([A-Za-z\u00C0-\u1FFF\u2800-\uFFFD]+)\s+\1(?:$|\s+?)/gi可以用来搜索文本中重复的单词。
分组
在了解正则表达式分组前先思考几个需求:
const htmlStr =
"content1
content2
";
-
将所有 p 标签替换为 span 标签
-
匹配非 p 或 br 的标签,全部替换为 div 标签
捕获型
一个捕获型分组是一个被包围在圆括号中的正则表达式分支。
任何匹配这个分组的字符都会被捕获。每个捕获型分组都被指定了一个数字,在正则表达式中第一个捕获(的是分组 1,第二个捕获(的是分组 2,以此类推。在正则表达式中\指向第 n 个分组,在正则表达式匹配后RegExp.$指向第 n 个分组。
由此可以解决需求 1:
htmlStr.replace(/<(\/?)p>/g, "<$1span>"); // content1
content2
但如果我匹配出
p而不想匹配出尖括号<>呢?
非捕获型
非捕获型分组有一个(?:前缀(捕获型仅为(前缀)。
非捕获型只做简单匹配,并不会捕获所匹配的文本,这会带来微弱的性能优势,且不会干扰捕获型分组的编号,即无法通过引用符\或$来对其引用(非捕获性)。
向前正向匹配(前瞻正向断言)
向前正向匹配分组有一个(?=前缀。
它类似于非捕获型分组,但在这个分组匹配成功后,文本会回到它开始的地方,实际上并不匹配任何东西。它只匹配一个位置,如同^匹配开头,$匹配结尾。
如:x(?=y)表示匹配x,仅在后面是y的情况
向前负向匹配(前瞻负向断言)
向前负向匹配分组有一个(?!前缀。
它类似于向前正向匹配分组,但只有当它匹配失败后,它才会继续向前匹配。
如:x(?!y)表示匹配x,仅在后面不是y的情况
由此可以解决需求 2:
htmlStr.replace(/<(\/?)(?!p|\/p|br\/?).*?>/g, "<$1div>"); // content1
content2
值得注意的是:对于匹配
p或者/p标签的规则(\/?)(?!p|\/p)中,|\/p规则是不能省略的,否则:"".match(/<(\/?)(?!p).*?>/g); // [""]这里正则向前匹配,第一个
标签匹配到标签没什么好说的;在第二个/时,由于后面是个向前负向匹配,需要对/进行前瞻断言,但是后面是字符p,所以不匹配/。此时文本会回到<重新断言(此时(\/?)匹配为空),后面是字符/p,故可以被该规则匹配。
向后正向匹配(后瞻正向断言)
向后正向匹配分组有一个(?<=前缀(es2018 才支持向后匹配)。
它类似于向前正向匹配,不过它是在相反的方向上进行条件判断。
如:(?<=y)x表示匹配x,仅在前面是y的情况
向后负向匹配(后瞻负向断言)
向后负向匹配分组有一个(?前缀。
如:(?表示匹配x,仅在前面不是y的情况
现在我们可以解决前面的一个问题了。如何不匹配出尖括号
<>:htmlStr.replace(/(?<=<)(\/?)p(?)/g, "$1span"); //content1
content2
这和上面的正则是一个效果。
字符集(类)
正则表达式字符集是一种指定一组字符的便利方式。
如:如果想匹配一个元音字母,我们可以写做
(?:a|e|i|o|u),但它可以被更方便地写成一个类[aeiou]。
另一个方便之处是类的求反。如果[后的第一个字符是^,那么这个类会排除这些特殊字符。
如:
[^!-\/:-@\[-`{-~]会匹配任何一个非 ASCII 特殊字符的字符。
字符转义
字符类内部的转义规则和正则表达式因子的相比稍有不同。
[\b]表示退格符(backspace)。下面是在字符类中需要被转义的特殊字符:
- \ / [ ] ^
量词
正则表达式因子可以用一个正则表达式量词后缀来决定这个因子应该被匹配的次数。
包围在一对花括号中的数字表示这个因子应该被匹配的次数。故,/www/和/w{3}/匹配的是一致的,{3,6}会匹配 3~6 次,{3,}会匹配 3 次或更多。
?等同于{0,1};*等同于{0,};+等同于{1,}
如果只有一个量词,表示趋向于进行贪婪匹配,即匹配尽可能多的副本直至上限;
如果这个量词附加一个后缀?,则表示趋向于进行非贪婪匹配,即只匹配必要的副本。
在上述需求 2的解决方案
htmlStr.replace(/<(\/?)(?!p|\/p|br\/?).*?>/g, "<$1div>");的.*?表示只匹配一个尖括号中的字符,否则该正则表达式将贪婪匹配整个字符串:htmlStr.replace(/<(\/?)(?!p|\/p|br\/?).*>/g, "<$1div>"); //