正则表达式的模式匹配
正则表达式(regular expression)是一个描述字符模式的对象。
推荐一个正则表达式的网站:https://regex101.com/
10.1 正则表达式的定义
Javascript中的正则表达式用RegExp对象表示。有如下方式进行创建:
- RegExp()构造函数
- 特殊的直接量语法,定义为包含在一对斜杠(/)之间的字符
//用来匹配以s结尾的字符串
var pattern = /s$/;
10.1.1 直接量字符
在正则表达式中,具有特殊含义的标点符号:
^ $ . * + ? = ! : | \ / ( ) [ ] { }
10.1.2 字符类
将直接量字符单独放进方括号内就组成了字符类(character class)。可以匹配它所包含的任意字符。
^
定义否定字符类,匹配所有不包含在方括号内的字符。
举例:
/[abc]/
Matches either an a, b or c character/[^abc]/
Matches any character except for an a, b or c/[a-zA-Z0-9]/
匹配拉丁字母(含所有大小写)和数字
由于某些字符类非常常用,所以使用了特殊字符的转义字符来表示他们:
字符 | 等价 | 匹配 |
---|---|---|
. | 除换行符和其他Unicode行终止符之外的任意字符 | |
\w | [a-zA-Z0-9] | 大小写拉丁字母及数字 |
\W | [^a-zA-Z0-9] | 任何不适ASCII字符组成的单词 |
\s | 任何Unicode空白符 | |
\S | 任何**非**Unicode空白符的字符 | |
\d | [0-9] | 任何ASCII数字 |
\D | [^0-9] | 除了ASCII数字之外的任何字符 |
\b | 退格直接量(特例) |
10.1.3 重复
在正则模式之后跟随用以指定字符重复的标记,如下:
字符 | 等价 | 含义 |
---|---|---|
{n,m} | 匹配前一项至少n次,但不能超过m次 | |
{n,} | 匹配前一项n次或更多次 | |
{n} | 匹配前一项n次 | |
? | {0,1} | 匹配前一项0次或1次 |
+ | {1,} | 匹配前一项1次或多次 |
* | {0,} | 匹配前一项0次或多次 |
示例:
/\d{2,4}/
匹配2-4个数字/\w{3}\d?/
匹配三个字母和可选的一个数字/\s+java\s+/
匹配前后带有一个或者多个空格的字符串“java”
注意:在使用*
和?
时要注意,由于这些字符可能匹配0个字符,因此他们允许什么都不匹配。例如,正则表达式/a*/
匹配字符串“bbbb”,因为这个字符串含有0个a。
10.1.4 选择、分组和引用
(1) 字符|
字符|
用于分隔供选择的字符。
例如:
/ab|cd|ef/
匹配“ab”或“cd”或“ef”/\d{3}|[a-z]{4}/
匹配3位数字或4个小写字母
注意,选择项的尝试匹配次序是从左到右,直到发现匹配项。一旦匹配上,就忽略之后的匹配项。例如,/a|ab/
匹配字符串“ab”时,只能匹配第一个字符。
(2) 圆括号
作用1:
把单独的项组合成子表达式,以便可以像处理一个独立的单元那样对单元内的项进行处理。
示例:
/java(script)?/
匹配字符串“java”,其后可以有“script”,也可以没有/(ab|cd)+|ef/
匹配字符串“ab”或者“cd”的一次或多次重复,也可匹配“ef”
作用2:
在完整的模式中定义子模式。当一个正则表达式成功地和目标字符串相匹配时,可以从目标串中抽出和圆括号中的子模式相匹配的部分。通过在字符\
后加一位或多位数字来实现。\3
引用第3个带圆括号的子表达式。
注意,嵌套表达式以左括号的位置来计算。例如:
/(java(script)?)\w*/
中嵌套的子表达式(script)可以用\2
指代。
对正则表达式中前一个子表达式的引用,并不是对字表达式的模式引用,而是与那个模式相匹配的文本引用。
例如,匹配位于单引号或者双引号内的0个或多个字符:
/(['"])[^'"]*\1/
10.1.5 指定匹配位置
指定匹配发生的合法位置的元素称为正则表达式的锚。
最常用的锚元素:^
匹配字符串的开始,$
匹配字符串的结束。
例子:
(1) /^java$/
:匹配前后都没有其他内容的java。
hello java, javasript world
java
(2) /\bjava\b/
:匹配java单词
hello java
javasript world
java
(3) /java(?=:)/
:匹配其后有冒号的java字符
hello java, javasript world
java
java: hello
(4) /java(?!:)/
:匹配其后没有冒号的java字符
hello java, **java**sript world
java
java: hello
锚字符汇总:
字符 | 含义 |
---|---|
^ | 匹配字符串的开头,在多行检索中,匹配一行的开头 |
$ | 匹配字符串的结尾,在多行检索中,匹配一行的结尾 |
\b | 匹配一个单词的边界,就是位于字符\w和\W之间的位置,或位于字符\w和字符串的开头或者结尾之间的位置(注意,10.1.2中介绍的[\b] 匹配的是退格符) |
\B | 匹配非单词边界的位置 |
(?=p) | 零宽正向先行断言,要求接下来的字符都与p匹配,但不能包括匹配p的那些字符 |
(?!p) | 零宽负向先行断言,要求接下来的字符都不与p匹配 |
10.1.6 修饰符
修饰符是放在第二条斜线之后的,用以说明高级匹配模式的规则。
字符 | 含义 |
---|---|
i | 执行不区分大小写的匹配 |
g | 执行一个全局匹配,即找到所有的匹配,而不是在找到第一个之后就停止 |
m | 多行匹配模式,^匹配一行的开头和字符串的开头,$匹配行的结束和字符串的结束 |
10.2 用于模式匹配的String方法
String支持如下4中正则表达式的方法:
(1) search()
参数:正则表达式
返回:第一个与之匹配的字符串的其实位置,如果找不到匹配项,返回-1
注意:该方法不支持全局检索,自动忽略修饰符g。
"JavaScript".search(/script/i); //=> 4
(2) replace()
参数1:正则表达式
参数2:用来替换的字符串
返回:替换后的字符串
//----例1----
//将不区分大小写的javascript替换成正确大小写的JavaScript
"javascript is JAVAscript".replace(/javascript/gi,"JavaScript"); //=> "JavaScript is JavaScript"
//----例2----
//将文本中的双引号改为中文的双引号
var text = 'java,"你好"';
var quote=/"([^"]*)"/g; //中间的内容区域不能包含引号
text.replace(quote, '“$1”'); //=> "java,“你好”"
如果在替换字符串中出现了$加数字,那么replace()将用与指定的子表达式相匹配的文本来替换这两个字符。
(3) match()
参数:正则表达式
返回:一个由匹配结果组成的数组
"1 plus 2 equals 3".match(/\d+/g) //=> ["1", "2", "3"]
"1 plus 2 equals 3".match(/\d+/) //=> ["1", index: 0, input: "1 plus 2 equals 3", groups: undefined]
(4) split()
参数:分隔符的正则表达式
返回:字符串数组
//指定分隔符可允许两边有任意多的空白符
"1 , 2,3,4".split(/\s*,\s*/); //=> ["1", "2", "3", "4"]
10.3 RegExp对象
正则表达式是通过RegExp对象来表示的。
(1) RegExp()构造函数
参数1:正则表达式主体部分,两条斜线之间的文本。(转义字符需要将\
替换为\\
)
参数2(可选):正则表达式修饰符
var zipcode = new RegExp("\\d{3}","g"); //=> /\d{3}/g
RegExp()构造函数非常有用,特别在需要动态创建正则表达式的时候。
10.3.1 RegExp的属性
示例都是以上文中定义的zipcode为例。
属性名 | 只读 | 含义 | 示例 |
---|---|---|---|
source | 是 | 包含正则表达式的文本 | zipcode.source; //=> "\d{3}" |
global | 是 | 正则表达式是否带有修饰符g | zipcode.global; //=> true |
ignoreCase | 是 | 正则表达式是否带有修饰符i | zipcode.global; //=> false |
multiline | 是 | 正则表达式是否带有修饰符m | zipcode.global; //=> false |
lastIndex | 否 | 如果匹配模式带有g修饰符,这个属性存储在整个字符串中下一次检索的开始位置 | zipcode.lastIndex; //=> 0 |
10.3.2 RegExp的方法
(1) exec()
对一个指定的字符串执行一个正则表达式。没有匹配返回null,找到一个匹配返回一个数组。
zipcode.exec("123,a,456,d"); //=> ["123", index: 0, input: "123,a,456,d", groups: undefined]
zipcode.lastIndex; //=> 3
zipcode.exec("123,a,456,d"); //=> ["456", index: 6, input: "123,a,456,d", groups: undefined]
zipcode.lastIndex; //=> 9
zipcode.exec("123,a,456,d"); //=> null
zipcode.lastIndex; //=> 0
第一次调用exec()时,匹配到了123,lastIndex为下一次检索开始的位置,则是3。第二次匹配后,需开始下次检索的位置变为9。当没有发现匹配结果后,lastIndex会重置为0。
(2) test()
参数为一个字符串,用test()对某个字符串进行检测,如果包含匹配的结果,则返回true。
zipcode.test("123,a,456,d"); //=>true
var pattern= /java/i;
pattern.test("Javascript"); //=> true