JavaScript 正则表达式

[参考]恩聪PHP,正则无“国”界。
一开始是这么觉得的,总结着总结着发现其实有些还是不一样的,于是结合菜鸟教程修正~

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

正则表达式的定义

直接定义变量:

1
var 变量名 = /正则表达式/attributes(可选);

创建RegExp对象:

1
var 变量名 = new RegExp(/正则表达式/, attributes(可选));

attributes是一个可选的字符串,可取值如下表:

字符 描述
i 匹配时不区分大小写
g 全局匹配(一般找到第一个匹配后停止)
m 多行匹配(默认正则将多行合并为一行进行匹配,所以待匹配字符串中包含换行符;如果加上m,则在匹配时按行分开匹配)

正则表达式的书写

正则表达式作为一个匹配的模板,是由原子(普通字符,例如字符a到z)、有特殊功能的字符(称为元字符,例如*、+和?等),以及模式修正符(即上文的attributes)三部分组成的文字模式。

原子

原子是正则表达式的最基本的组成单位,包括未显示指定为元字符的打印和非打印字符。

普通字符

大小写字母字符、所有数字等。如:

1
2
/5/      // 匹配字符串中是否有5这个数字
/abc/ // 匹配字符串中是否有abc这串字符

特殊字符和元字符

任何一个符号都可以作为原子使用,但如果这个符号在正则表达式中有一些特殊意义,就必须使用转义字符 \ 取消它的特殊意义,将其变成一个普通的原子。如:

1
2
3
4
5
6
/\"/    // 双引号
/\'/ // 单引号
/\+/ // 加号
/\*/ // 乘号
/\./ // 英文句号
/\<br\/\>/ // 尖括号、斜杠等,匹配字符串中是否有HTML的<br/>标记字符

试一试:

非打印字符

字符串中的格式控制符号,例如空格、回车及制表符号等。

字符 描述
\f 匹配一个换页符,等价于 \x0c 和 \cL
\n 匹配一个换行符,等价于 \x0a 和 \cJ
\r 匹配一个回车符,等价于 \x0d 和 \cM
\t 匹配一个制表符,等价于 \x09 和 \cI
\v 匹配一个垂直制表符,等价于 \x0b 和 \cK

通用字符类型

前面介绍的三类字符作为原子,都是一个原子匹配一个字符。而有时我们需要一个原子匹配一类字符,例如匹配所有数字,或者匹配所有字母,这时就要使用“通用字符类型”了。

字符 描述
\d 匹配任意一个十进制数字,等价于[0-9]
\D 匹配任意一个除十进制数字以外的字符,等价于[^0-9]
\s 匹配任意一个空白字符,包括空格、制表符、换页符等,等价于 [\f\n\r\t\v]
\S 匹配任意一个空白字符,等价于 [^\f\n\r\t\v]
\w 匹配任意一个数字、字母或下划线,等价于[0-9a-zA-Z]
\W 匹配任意一个数字、字母或下划线的字符,等价于[^0-9a-zA-Z]

自定义原子表

通用字符类型(类原子)的自定义扩充。
通过原子表[···]匹配一个属于原子表内的字符;
通过原子表[^···]匹配排除原子表中所有原子的一个字符。
如:

1
2
3
/[apj]sp/     // 匹配asp、psp、jsp三种字符串
/[^apj]sp/ // 匹配除asp、psp、jsp三种以外的*sp字符串,如:xsp、ysp等
/0[xX][0-9a-fA-f]+/ // 匹配十六进制数,如:0x2f、0X3AE等

试一试:

元字符

元字符用于修饰原子,不能单独出现。

限定符

用来指定正则表达式的一个给定原子必须要出现多少次才能满足匹配。

字符 描述
* 匹配前面的子表达式零次或多次,等价于{0,}
+ 匹配前面的子表达式一次或多次,等价于{1,}
? 匹配前面的子表达式零次或一次,等价于{0,1}
{n} n是一个非负整数,匹配确定的n次(子字符串连续出现n次,中间不能间断)
{n,} n是一个非负整数,至少匹配n次
{n,m} m和n均为非负整数,且n<=m,最少匹配n次且最多匹配m次

边界限制

用来限定字符串或单词的边界范围,以获得更准确的匹配结果。

字符 描述
^ 匹配输入字符串的开始位置
$ 匹配输入字符串的结束位置
\b 匹配一个单词边界(可以是前边界,也可以是后边界),也就是指单词和空格间的位置。
\B 匹配非单词边界

如:

1
2
3
/^this/     // 匹配以 "this" 开头的字符串
/\bis\b/ // 只能匹配 "is"
/\Bis/ // 不能匹配 "is" 打头的单词,可以匹配 "this"、"thisth" 等

试一试:

英文句号

匹配目标中的任何一个字符,包括不可打印字符,但默认换行符除外。通常,可以使用.*?.+?组合来匹配除换行符以外的任何字符串。如:
*+后面加?是什么情况?

1
2
/a.b/    // 匹配在a和b之间有任意一个字符的字符串,例如axb、ayb、azb等
/<b>.*?<\/b>/ // 匹配以<b>标签开始、</b>标签结束的任何不包括换行符的字符串

试一试:

模式选择符

竖线字符|用于分隔两个可以匹配的选项。如:

1
/Linux|Apache|MySQL|PHP/    // 匹配Linux、Apache、MySQL、PHP中的一种

模式选择符的运算优先级是最低的,所以不会变成仅分隔左右两个字母的情况。(相当于Linux、Apache、MySQL、PHP都套上了括号)

试一试:

模式单元

使用元字符()将多个原子组成一个大原子,将其当作一个单元独立使用,与数学表达式中的括号作用类似。如:

1
/(very)*good/    // 匹配good、very good、very very good等

后向引用

元字符()包裹单元匹配到的字符串会存储到一个临时缓冲区中,之后可以通过\n再次引用匹配。如:

1
2
/^\d{4}\W\d{2}\W\d{2}$/      // 匹配日期的格式,如2008-08/08、2008/08-08等
/^\d{4}(\W)\d{2}\1\d{2}$/ // 匹配日期的格式,如2008-08-08、2008/08/08等

当需要使用模式单元()而又不想形成临时缓冲区时,可以使用非获取匹配,有以下三种非匹配获取:

1) (?:pattern):匹配pattern但不对匹配结果进行存储。如:

1
2
/(Windows)(Linux)\\2OS/       // 使用 "\2" 再次引用第二个缓冲区中的字符串Linux
/(?:Windows)(Linux)\\1OS/ // 使用了 "?:" 忽略了第一个子表达式的存储,所以 "\1" 引用的就是Linux

2) (?=pattern):正向预查,在任何匹配pattern的字符串开始处匹配查找字符串。如:

1
/Windows(?=95|98|NT|2000)/    // 可以匹配Windows2000中的Windows,但不能匹配Windows3.1中的Windows。

3) (?!pattern):负向预查,在任何不匹配 pattern 的字符串开始处匹配查找字符串。如:

1
/Windows(?!95|98|NT|2000)/    // 可以匹配Windows3.1中的Windows,但不能匹配Windows2000中的Windows。

试一试:

运算符优先级

相同优先级的从左到右进行运算,不同优先级的运算先高后低。下表按优先级由高到低展示:

优先级 运算符 描述
1 |转义符
2 (pattern)、(?:pattern)、(?=pattern)、(?!pattern)、[] 模式单元与原子表
3 *、+、?、{n}、{n,}、{n,m} 限定符
4 ^、 $、原子字符等 边界限制等
5 竖线字符 模式选择符,或

正则表达式的使用

[参考]w3school-JavaScript RegExp对象

RegExp对象属性:

属性 描述
global RegExp对象是否具有标志g
ignoreCase RegExp对象是否具有标志i
multiline RegExp对象是否具有标志m
lastIndex 返回一个整数,标示开始下一次匹配的起始字符位置
source 正则表达式的源文本

关于lastIndex的补充说明:
上次匹配的结果是由方法RegExp.exec()和RegExp.test()找到,它们都以lastIndex属性所指的位置作为下次检索的起始点。这样,就可以通过反复调用这两个方法来遍历一个字符串中的所有匹配文本。
该属性是可读可写的。只要目标字符串的下一次搜索开始,就可以对它进行设置。当方法exec()或test()再也找不到可以匹配的文本时,它们会自动把lastIndex属性重置为0。
不具有标志g和不表示全局模式的RegExp对象不能使用lastIndex属性。
如果在成功地匹配了某个字符串之后就开始检索另一个新的字符串,需要手动地把这个属性设置为0。

试一试:

RegExp对象方法:

属性 描述
compile 编译正则表达式
exec 检索字符串中指定的值,返回找到的值,并确定其位置等(与match相似)
test 检索字符串中指定的值是否存在,返回true或false

关于exec的补充描述:经尝试,exec似乎没有全局搜索出多个匹配结果的能力,其结果与match非全局搜索时的结果一致,但是match全局搜索获得的是数组形式。

试一试:

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

属性 描述
search 检索与正则表达式相匹配的值,返回第一个匹配的位置(忽略全局搜索。如果没有搜到,返回-1)
match 找到一个或多个正则表达式的匹配,返回匹配值(全局搜索只返回匹配值;非全局搜索返回[匹配值, 位置, 被查找的字符串]。如果没有搜到,返回null)
replace 替换与正则表达式匹配的子串:
stringObject.replace(regexp/substr, replacement)
split 把字符串分割为字符串数组:
stringObject.split(separator, [howmany])
(separator为字符串或正则表达式,howmany为返回的数组的最大长度,可选,默认全部返回)

试一试:

正则表达式的三种模式

[参考]正则表达式的三种模式【贪婪、勉强、侵占】的分析

正则表达式的三种模式分别为贪婪模式.*、勉强模式.*?以及侵占模式.*+。假设有一字符串:xfooxxxxxxfoo

1) 贪婪模式 .*foo

正则表达式的默认模式。匹配从多到少依次尝试,直到匹配成功或匹配完所有可能。贪婪模式下的匹配结果为:xfooxxxxxxfoo。

2) 勉强模式 .*?foo

最小匹配方式,匹配从最少开始尝试,直到匹配成功。勉强模式下的匹配结果为:xfoo, xxxxxxfoo。

3) 侵占模式 .*+foo

又称占用模式,匹配以最多匹配的角度出发,一旦匹配成功就不再返回尝试,所以即使后面的项不能匹配,也直接以匹配失败结束。侵占模式下的匹配结果为:null。
实际尝试时报错……似乎不能识别这种表达形式

试一试: