一组由字母和符号组成的特殊文本,它可以用来从文本中找出满足你想要的格式的句子。
基本匹配
前面说到正则表达式的组成,一个最简单的正则表达式:what,它表示一个规则:由字母 w开始,接着是 h,然后 a,最后 t:
let reg = new RegExp('what');
reg.test(`what's your name?`); // true
let reg1 = new RegExp(/what/);
reg1.test(`what's your name?`); // true
reg1.test(`What's your name?`); // false
可以看到正则表达式大小写敏感。我们之后也会说到如何去处理大小写敏感这个问题。
元字符
正则表达式的精彩部分-元字符,元字符不代表他们本身的意思,正则表达式赋予了它们一些特殊的含义在其中:
| 元字符 | 描述 |
|---|---|
. |
英文句号匹配任意单个字符,除了换行符 |
[] |
字符种类。匹配方括号内的任意字符。 |
[^] |
否定的字符种类。匹配除了方括号内的任意字符 |
* |
匹配>=0个重复的在*号之前的任意字符 |
+ |
匹配>=1个重复的在+ 号前的字符 |
? |
标记?之前的字符为可选 |
{n,m} |
匹配num个大括号之前的字符(n<=num<=m) |
(xyz) |
字符集,匹配与 xyz 完全相等的字符串 |
| |
或运算符,匹配符号前或后的字符 |
\ |
转义字符,用于匹配一些保留的字符[](){}.*+?^$\| |
^ |
从开始行进行匹配,与方括号内的[^]代表的意义不同 |
$ |
从末端开始匹配 |
2.1 点运算符
如上所说,.匹配任意字符,但不匹配换行符:
let text = 'password1234';
let point = new RegExp('/./g');
text.match(/./g); //["p", "a", "s", "s", "w", "o", "r", "d", "1", "2", "3", "4"]
text.match(point); // 与上述结果相同
point.test(text); // true
2.2 字符集
在正则表达式中,方括号[] 用来指定一个字符集。在方括号中使用连字符 - 来指定字符集的范围。在方括号中的字符集不关心顺序:
let text = "The car parked in the garage."
text.match('[tT]he'); //["The", index: 0, input: "The car parked in the garage.", groups: undefined]
text.match(/[tT]he/g); //["The", "the"];
// 效果同或(|)符号 - 注意写法
text.match(/(t|T)he/g); //["The", "the"];
方括号内的句号就表示句号:
let text = "The car parked in the garage."
text.match("ge[.]"); //["ge.", index: 26, input: "The car parked in the garage.", groups: undefined]
text = "The car parked in the garage"
text.match("ge[.]"); // null
2.2.1 否定字符集
一般来说^ 表示一个字符串的开头(下文会提到这种用法),但它用在一个方括号的开头的时候,它表示这个字符集是否定的。例如:
let text = "The car parked in the garage.";
let square = new RegExp('[^c]ar');
text.match(square); //["par", index: 8, input: "The car parked in the garage", groups: undefined]
text.match(/[^c]ar/g); //["par", "gar"]
2.3 重复次数
后面跟着元字符+ ,* 以及? 的,是用来指定匹配子模式的次数。这些元字符在不同的情况下代表着不一样的意思 。
2.3.1 * 元字符
* 元字符匹配在 * 之前出现**大于等于0次(>=)**的字符。例如,表达式a* ,匹配以 0个 或更多个a开头的字符,因为有 0个 这样的条件,其实也就匹配了所有的字符:
let text = "The car parked in the garage.";
text.match(/[a-z]*/g); //(14) ["", "he", "", "car", "", "parked", "", "in", "", "the", "", "garage", "", ""]
text.match(/(c)*ar/g); //["car", "ar", "ar"]
*元字符以及.元字符搭配可以匹配所有的字符.*。*和表示匹配空格的符号\s连起来用, 如表达式\s*cat\s*匹配 0个 或更多个空格开头和 0个 或更多个空格结尾的cat字符串:
let text = "The fat cat sat on the concatenation.";
text.match(/\s*cat\s*/g); //[" cat ", "cat"]
2.3.2 + 元字符
+元字符匹配+之前的出现大于等于1次(>=1) ,例如:
let text = "The fat cat sat on the mat.";
text.match(/c.+t/g); //["cat sat on the mat"]
2.3.3 ?元字符
?元字符表示在 ? 之前的符号字符为可选,即出现 0次 或 1次 。例如:
let text = "The car is parked in the garage.";
text.match(/[T]?he/g); // ["The", "he"]
2.4 {}号
在正则表达式中,{} 是一个量词,常用来表示一个或一组字符可以出现的次数:
let text = "The number was 9.9997 but we rounded it off to 10.0.";
text.match(/[0-9]{2,3}/g); //["999", "10"]
匹配的是最少两位,最多三位的0~9的数字。
如果省略第二个参数,则是匹配至少第一个参数的0~9的数字,上述就是匹配至少2位0~9的数字:
let text = "The number was 9.9997 but we rounded it off to 10.0.";
text.match(/[0-9]{2,}/g); //["9997","10"]
注意⚠️这里是省略第二个参数,但是参数之间的,还在。如果此{} 里面只有一个参数,那代表的是固定重复次数的匹配:
let text = "The number was 9.9997 but we rounded it off to 10.0.";
text.match(/[0-9]{3}/g); //["999"]
2.5 | 运算符
表示逻辑或,用来判断条件:
let text = "The car is parked in the garage.";
text.match(/(t|T)he/g); //["The", "the"]
2.6 (...) 特征标群
特征标群是一组写在 (...) 中的子模式。像之前所提到的{} 是用来表示前面一个字符出现指定次数。但如果在{}前加入特征标群则表示整个标群内的字符出重复 N 次。例如表达式 (ab)* 匹配连续出现 0 或更多个 ab .
我们还可以在() 中用或字符 | 表示或。比如,(c|g|p)ar 匹配 car或 gar或 par :
let text = "The car is parked in the garage.";
text.match(/(c|p|g)ar/g); //["car", "par", "gar"]
2.7 转码字符串
反斜线 \ 在表达式中用于转码紧跟其后的字符. 用于指定 { } [ ] / \ + * . $ ^ | ? 这些特殊字符. 如果想要匹配这些特殊字符则要在其前面加上反斜线 \.
例如 . 是用来匹配除换行符外的所有字符的. 如果想要匹配句子中的 . 则要写成 \. 以下这个例子 \.?是选择性匹配. :
let text = "The fat cat sat on the mat.";
text.match(/(f|c|m)at\.?/g); //["fat", "cat", "mat."]
2.8 锚点
在正则表达式中,想要匹配指定开头或结尾的字符串就要使用到锚点。^ 指定开头,$ 指定结尾:
2.8.1 ^ 号
^ 符号用来检查匹配的字符串是否在所匹配字符串的开头。
let text = "The car is parked in the garage.";
text.match(/^(t|T)he/g); //["The"]
可以看到这次只匹配到了一个符合条件的字符The,这是因为针对要匹配的字符,只在头部进行匹配是否符合表达式的条件。
2.8.2 $ 号
同 ^ 原理相似,$ 符号用来匹配字符是否是最后一个:
let text = "The fat cat sat on the mat.";
text.match(/((f|c|m)at\.?)$/g); //["mat."]
这次只匹配了字符串最后的符合正则表达式条件的字符。
3. 简写字符集
在上述示例中,我们看到过 [0-9] [a-z] 这样的表达式,分别代表着匹配数字,匹配所有的小写字母,在实际的生活中我们会经常使用这样的匹配条件,为了使用方便,正则表达式中内置了一些简写的字符集来表达此类条件:
| 简写 | 描述 |
|---|---|
. |
除换行符外的所有字符 |
\w |
匹配所有字母数字,等同于[a-zA-Z0-9_] (小写字母/大写字母/数字/下划线) |
\W |
匹配所有非字母数字,及符号,等同于[^\w] |
\d |
匹配数字: [0-9] |
\D |
匹配非数字: [^\d] |
\s |
匹配所有空格字符:[\t\n\f\r\p{Z}] |
\S |
匹配所有非空格字符: [^\s] |
\f |
匹配一个换页符 |
\n |
匹配一个换行符 |
\r |
匹配一个回车符 |
\t |
匹配一个制表符 |
\v |
匹配一个垂直制表符 |
\p |
匹配 CR/LF(等同于\r\n),用来匹配 DOS行终止符 |
4. 零宽度断言(前后预查)
先行断言和后发断言都属于非捕获簇(不捕获文本,也不针对组合进行计数)。先行断言用于判断所匹配的格式是否在另一个确定的格式之前,匹配结果不包含该确定格式(仅作为约束):
| 符号 | 描述 |
|---|---|
?= |
正先行断言-存在 |
?! |
负先行断言-排除 |
?<= |
正后发断言-存在 |
?<! |
负后发断言-排除 |
4.1 ?=...正先行断言
?=... 正先行断言,表示第一部分表达式之后必须跟着?=定义的表达式。
返回结果只包含满足匹配条件的第一部分表达式。定义一个正先行断言要使用()括号。在括号内部使用一个问号和等号(?=...) .
正先行断言的内容写在括号中的等号后面:
let text = "The fat cat sat on the mat.";
text.match(/(t|T)he(?=\sfat)/g); //["The"]
//让我们看一下不加正弦断言的结果是:
text.match(/(t|T)he/g); //["The", "the"]
可以很明显的分析出来加了正先行断言所带来的结果的变化.
4.2 ?!...负先行断言
负先行断言?!用于筛选所有撇配结果,筛选条件位其后不跟随着断言中定义的格式。与正先行断言的区别就是?=问号后的符号的改变为 ?!
let text = "The fat cat sat on the mat.";
text.match(/(t|T)he(?!\sfat)/g); //["the"]
4.3 ?<=... 正后发断言
正后发断言,使用?<=... 用于筛选所有皮配结果,筛选条件为其前不跟随断言中定义的格式:
let text = "The fat cat sat on the mat.";
text.match(/(?<=(t|T)he\s)(fat|mat)/g); //["fat", "mat"]
text.match(/(t|T)he\s(fat|mat)/g); //["The fat", "the mat"]
4.4 ?<!负后发断言
负后发断言 记作 (?<!...) 用于筛选所有匹配结果, 筛选条件为 其前不跟随着断言中定义的格式:
let text = "The fat cat sat on the mat.";
text.match(/(?<!(T|t)he\s)(cat)/g); //["cat"]
5. 标志
标志也叫模式修正符, 因为它可以用来修改表达式的搜索结果. 这些标志可以任意的组合使用, 它也是整个正则表达式的一部分.
| 标志 | 描述 |
|---|---|
i |
忽略大小写 |
g |
全局搜索 |
m |
多行的:锚点元字符^ $ 工作范围在每行的起始。 |
5.1 全局搜索
在上面的例子中已经看到很多关于全局标志符的使用,这里在对比一下使用和不使用全局标志符的差别:
let text = "The fat cat sat on the mat.";
text.match(/.(at)/); //["fat", "at", index: 4, input: "The fat cat sat on the mat.", groups: undefined]
text.match(/.(at)/g); //["fat", "cat", "sat", "mat"]
5.2 忽略大小写
忽略大小写的效果我们在上文也了解过,不过是另外一种复杂的写法,现在来使用 i 标志对比一下两种写法:
let text = "The fat cat sat on the mat.";
text.match(/(t|T)he/gi); //["The", "the"]
text.match(/The/gi); //["The", "the"]
text.match(/the/gi); //["The", "the"]
5.3 多行修饰符
多行修饰符 m 常用于执行一个多行匹配.
像之前介绍的 (^,$) 用于检查格式是否是在待检测字符串的开头或结尾. 但我们如果想要它在每行的开头和结尾生效, 我们需要用到多行修饰符 m.
6. 贪婪匹配与惰性匹配
正则表达式默认采用贪婪匹配模式,在该模式下意味着会匹配尽可能长的子串。我们可以使用 ? 将贪婪匹配模式转化为惰性匹配模式。
let text = "The fat cat sat on the mat.";
text.match(/(.*at)/); //["The fat cat sat on the mat", "The fat cat sat on the mat", index: 0, input: "The fat cat sat on the mat.", groups: undefined]
text.match(/(.*?at)/); //["The fat", "The fat", index: 0, input: "The fat cat sat on the mat.", groups: undefined]