阅读(88) (2)

JavaScript学习笔记整理(13):正则表达式

2017-06-19 13:51:39 更新

正则表达式(regular expression)是一个描述字符模式的对象。JavaScript的RegExp类表示正则表达式,String和RegExp都定义了方法。


1.1 正则表达式的定义
正则表达式有两种方法定义:
一种是使用正则表达式直接量,将其包含在一对斜杠(/)之间的字符。

var pattern = /s$/;

另一种是使用RegExp()构造函数。

var pattern = new RegExp('s');

上面两种方法是等价的,用来匹配所有以字母“s”结尾的字符串。

正则表达式的模式规则是由一个字符序列组成的,所有字母和数字都是按照字面含义进行匹配的。

1.1.1 直接量字符
JavaScript正则表达式语法也支持非字母的字符匹配,这些字符需要通过反斜杠(\)作为前缀进行转义。
有如下直接量字符:

\0 匹配null字符(\u0000)。

[\b] 匹配退格键(\u0008),不要与\b混淆。

\t 匹配制表符tab(\u0009)。  

\n 匹配换行键(\u000A)。 

\v 匹配垂直制表符(\u000B)。  

\f 匹配换页符(\u000C)。 

\r 匹配回车键(\u000D)。       

\xnn 匹配一个以两位十六进制数(\x00-\xFF)表示的字符。  

\uxxx 匹配一个以四位十六进制数(\u0000-\uFFFF)表示的unicode字符。

\cX 表示Ctrl-[X],其中的X是A-Z之中任一个英文字母,用来匹配控制字符。


1.1.2 字符类
将直接量字符单独放进方括号就组成了字符类(character class)。一个字符类可以匹配它所包含的任意字符。比如:/[abc]/就和字母“a”、“b”、“c”中的任意一个都匹配。

我们还可以通过“^”符号来定义否定字符类,它匹配所有不包含括号内的字符。定义否定字符类时,将一个“^”符号作为左方括号内的第一个字符。比如: /[^abc]/ 匹配的是“a”、“b”、“c”之外的所有字符。’

字符类还可以使用连字符来表示字符范围。比如要匹配拉丁字母表中的小写字母,可以使用 /[a-z]/ ,要匹配拉丁字母表中的任何字母和数字,则使用 /[a-zA-Z0-9]/ 。

注意:字符类的连字符必须在头尾两个字符中间,才有特殊含义,否则就是字面含义。比如,[-9]就表示匹配连字符和9,而不是匹配0到9。

正则表达式的字符类:

[...]  方括号内的任意字符

[^...]不在方括号内的任意字符

.   除换行符和其他Unicode行终止符之外的任意字符

\w  任何ASCII字符组成的单词,等价于[a-zA-Z0-9]

\W  任何不适ASCII字符组成的单词,等价于[^a-zA-Z0-9]

\s  任何Unicode空白符

\S  任何非Unicode空白符的字符

\d  任何非ASCII数字,等价于[0-9]

\D  除了ASCII数字之外的任何字符,等价于[^0-9]

[\b]  退格直接量

注意:在方括号之内也可以写上面这些特殊转义字符。但有一个特例:当转义符 \b 用在字符类中时,它表示的是退格符,所以要在正则表达式中按照直接量表示一个退格符时,只需使用 /[\b]/ 即可。
1.1.3 重复
在正则模式之后跟随用以指定字符重复的标记。

正则表达式的重复字符语法:

{n,m}  匹配前一项至少n次,至多m次

{n,}   匹配前一项n次或者更多次,也可以说至少n次

{n}    匹配前一项n次

?      匹配前一项0次或者1次,等价于{0,1}

+      匹配前一项1次或多次,等价于{1,}

*      匹配前一项0次或多次,等价于{0,}

注意:在使用“*”和“?”时,由于这些字符可能匹配0个字符,因此它们允许什么都不匹配。比如:正则表达式/a*/实际上与字符串“bbb”匹配,因为这个字符串含有0个a。

1.1.4 非贪婪的重复
默认情况下,匹配重复字符是尽可能多的匹配,而且允许后续的正则表达式继续匹配,即匹配直到下一个字符不满足匹配规则为止,这称为“贪婪的”匹配。

当然,我们也可以使用正则表达式进行非贪婪匹配,一旦条件满足,就不再往下匹配。,只须在待匹配的字符后跟随一个问号即可:“??”、“+?”、“*?”或“{1,5}?”。

/a+/.exec('aaa')  //["aaa"]

/a+?/.exec('aaa')  //["a"]


1.1.5 选择、分组和引用
正则表达式的语法还包括指定选择项、子表达式分组和引用前一子表达式的特殊字符。

字符“|”用于分隔供选择的字符,比如:/ab|cd|ef/可以匹配字符串“ab”,也可以匹配字符串“cd”,还可以匹配字符串“ef”。

注意:选择项的尝试匹配次序是从左到右,直到发现了匹配项。如果左边的选择项匹配,就会忽略右边的匹配项,即使它产生更好的匹配。比如:当正在表达式/a|ab/匹配字符串“ab”时,它只能匹配第一个字符。

圆括号“()”可以把单独的项组合成子表达式。

带圆括号的表达式的另一个用途是允许在同一正则表达式的后部引用前面的子表达式,这是通过在字符“\”后加一位或多位数字来实现的,这个数字指定了带圆括号的子表达式在正则表达式中的位置。比如:\1引用的是第一个带圆括号的子表达式,\3引用的是第三个带圆括号的子表达式。

/(.)b(.)\1b\2/.test('abcabc')  //true

上面的代码中,\1表示第一个括号匹配的内容,即第一个(.),匹配的是“a”;\2表示第二个括号(.),匹配的是“b”。

注意:因为子表达式可以嵌套另一个子表达式,所以引用的位置是参与计数的左括号的位置。比如:(s(ss)),\2表示的是(ss)。

对正则表达式中前一个子表达式的引用,并不是指对子表达式模式的引用,而指的是那个模式相匹配的文本的引用。

/(a|b)c\1/.test('aca')  //true

/(a|b)c\1/.test('acb')  //false

上面的代码中,由于(a|b)匹配的是a,所以\1所对应的也应该是a。

正则表达式的选择、分组和引用字符

|    选择,匹配的是该符号左边的子表达式或右边的子表达式

(...)    组合,将几个项组合为一个单位,这个单位可通过“*”、“+”、“?”和“|”等符号加以修饰,而且可以记住和这个组合相匹配的字符串以供此后的引用使用。

(?...)    只组合,把项组合到一个单位,但不记忆与该组相匹配的字符

\n    和第n个分组第一次匹配的字符相匹配,组是圆括号中的子表达式(也有可能是嵌套的),组索引是从左到右的左括号数,“(?:”形式的分组不编码


1.1.6 指定匹配位置

除了匹配字符串中的字符外,有些正则表达式的元素匹配的是字符之间的位置,亦可称为正则表达式的。比如:\b匹配一个单词的边界,即位于\w(ASCII单词)字符和\W(非ASCII单词)之间的边界,或位于一个ASCII单词与字符串的开始或结尾之间的边界。


正则表达式中的锚字符:

^    匹配字符串的开头,在多行检索中,匹配一行的开头

$    匹配字符串的结尾,在多行检索中,匹配一行的结尾

\b   匹配一个单词的边界

\B   匹配非单词边界的位置

(?=p)  零宽正向先行断言,要求接下来的字符都与p匹配,但不能包括匹配p的那些字符

(?!p)  零宽负向先行断言,要求接下来的字符不与p匹配


1.1.7  修饰符

正则表达式的修饰符,用以说明高级匹配模式的规则。修饰符是放在“/”符号之外的,也就是说,它们不是出现在两条斜杠之间的,而是在第二条斜杠之后。


在JavaScript中,支持三个修饰符:

i   执行不区分大小写的匹配

g   执行一个全局匹配,即找到所有的匹配,而不是在找到第一个之后就停止

m   多行模式匹配,在这种模式下,如果待检索的字符串包含多行,那么^和$锚字符除了匹配整个字符串的开始和结尾之外,还能匹配每行的开始和结尾

这些修饰符可以任意组合。比如:

/test/ig


1.2 用于模式匹配的String方法

String支持4种使用正则表达式的方法。

search():按照给定的正则表达式进行搜索,返回一个整数,表示第一个与之匹配的字符串的起始位置,如果找不到匹配的子串,将返回-1。

match():返回一个数组,成员是所有匹配的子字符串。    

replace():按照给定的正则表达式进行替换,返回替换后的字符串。  

split():按照给定规则进行字符串分割,返回一个数组,包含分割后的各个成员。

1.2.1 search()

"javascript".search(/script/i);

上面的代码的返回值为4


如果search()的参数不是正则表达式,则首先会通过RegExp构造函数将它转换成正则表达式,search()方法不支持全局检索,因为它忽略正则表达式参数中的修饰符g。


1.2.2 match()

match()方法的唯一参数是一个正则表达式,返回的是一个由匹配结果组成的数组。如果该正则表达式设置了修饰符g,则返回的数组包含字符串中的所有匹配结果。

'1 plus 2 equals 3'.match(/\d+/g)  //返回["1","2","3"]

返回来的数组还带有另外两个属性:index和input,分别表示包含发生匹配的字符位置和引用的正在检索的字符串。


1.2.3 replace()

replace()方法用以执行检索与替换操作。其中第一个参数是一个正则表达式,第二个参数是要进行替换的字符串。


如果replace()的第一个参数是字符串而不是正则表达式,则replace()将直接搜索这个字符串,而不会像search()一样首先通过RegExp()将它转换为正则表达式。


replace方法的第二个参数可以使用美元符号$,用来指代所替换的内容。

$& 指代匹配的子字符串。  

$` 指代匹配结果前面的文本。  

$' 指代匹配结果后面的文本。  

$n 指代匹配成功的第n组内容,n是从1开始的自然数。  

$$ 指代美元符号$。

比如:

'hello world'.replace(/(\w+)\s(\w+)/, '$2 $1')  

// "world hello"

replace方法的第二个参数还可以是一个函数,将每一个匹配内容替换为函数返回值。

'abca'.replace(/a/g,function(match){   

  return match.toUpperCase();   

});

// "AbcA"

replace()方法的第二个参数可以接受多个参数。第一个参数是捕捉到的内容,第二个参数是捕捉到的组匹配(有多少个组匹配,就有多少个对应的参数)。


1.2.4 split()

split()方法用以将调用它的字符串拆分为一个子串组成的数组。

'123,456,789'.split(',')  //返回["123","456","789"]

split()方法的参数也可以是一个正则表达式。


1.3 RegExp对象

RegExp()构造函数带有两个字符串参数,第二个参数是可选的,它指定正则表达式的修饰符(可传入修饰符g、i、m或者它们的组合);第一个参数包含正则表达式的主体部分,也就是正则表达式直接量中两条斜线之间的文本。

var regexp = new RegExp('\\d{5}','g')

上面的代码表示会全局的查找5个数字。


1.3.1 RegExp对象的属性

每个RegExp对象都包含5个属性:

source    只读字符串,包含正则表达式的文本

global    只读布尔值,用以说明这个正则表达式是否带有修饰符g

ignoreCase    只读布尔值,用以说明正则表达式是否带有修饰符i

multiline    只读布尔值,用以说明正则表达式是否带有修饰符m

lastIndex    可读写的整数,如果匹配模式带有g修饰符,这个属性存储在整个字符串中下一次检索的开始位置。


1.3.2 RegExp的方法

RegExp对象定义了两个用于执行模式匹配操作的方法。


(1)exec()

正则对象的exec方法,可以返回匹配结果。如果发现匹配,就返回一个数组,成员是每一个匹配成功的子字符串,否则返回null。

/a|b|c/.exec('abc')  // ["a"]


/a|b|c/.exec('qwe')  // null


(2)test()

正则对象的test方法返回一个布尔值,表示当前模式是否能匹配参数字符串。

var s = /a/g;   

var a = 'baba';   

s.lastIndex   //0

s.test(a);   //true

  

s.lastIndex;   //2

s.test(a);  //true

注意:如果正则表达式带有g修饰符,则每一次test方法都从上一次结束的位置开始向后匹配,也可以通过正则对象的lastIndex属性指定开始搜索的位置。