正则表达式学习--javascript权威指南
JavaScript中的正则表达式语法是Perl5的正则表达式语法的大型子集。有一些Perl正则表达式语法特性并不被ECMAScript支持,这些特性包括:s(单行模式)和x(扩展语法)标记;\a \e \l \u \L \U \E \Q \A \Z \z和\G转义字符;"(?<="正向后行断言和"(?<!"负向后行断言;"(?#"注释和扩展"(?"的语法。
正则表达式模式规则是由一个字符序列组成的。包括所有字母和数字在内,大多数字符都是按照直接量仅描述带匹配的字符的。如此说来,正则表达式/java/可以匹配任何包含"java"子串的字符串。除此之外,正则表达式中还有其他具有特殊语义的字符,这些字符不按照字面含义进行匹配。比如,正则表达式/s$/包含两个字符,第一个字符"s"按照字面含义匹配,第二个字符$是一个具有特殊语义的元字符,用以匹配字符串的结束。因此这个正则表达式可以匹配任何以"s"结束的字符串。
正则表达式中所有的字母和数字都是按照字面含义进行匹配的。JavaScript正则表达式语法也支持非字母的字符匹配,这些字符需要通过反斜线(\)作为前缀进行转义。比如,转义字符\n用意匹配换行符。
下表:正则表达式中的直接量字符:
字符 | 匹配 |
字母和数字 | 自身 |
\o | NUL字符(\u0000) |
\t | 制表符(\u0009) |
\n | 换行符(\u000A) |
\v | 垂直制表符(\u000B) |
\f | 换页符(\u000C) |
\r | 回车符(\u000D) |
\xnn | 由十六进制数nn指定的拉丁字符,例如,\x0A等价于\n |
\uxxxx | 由十六进制数xxxx指定的Unicode字符,例如\u0009等价于\t |
\cX | 控制字符^X,例如,\cJ等价于换行符\n |
在正则表达式中,许多标点符号具有特殊含义,它们是:
^ $ . * + ? = ! : | \ / ( ) [ ] { }
某些符号只有在正则表达式的某些上下文中才具有某种特殊含义,在其他上下文中则被当成直接量处理。然而,如果想在正则表达式中使用这些字符的直接量进行匹配,则必须使用前缀\,这是一条通行规则。其他标点符号(比如@和引号)没有特殊含义,在正则表达式中按照字面含义进行匹配。
一、字符类
将直接量字符单独放进方括号内就组成了字符类(character class)。一个字符类可以匹配它所包含的任意字符。因此正则表达式/[abc]/就和字母"a"、"b"、"c"中的任意一个都匹配。另外,可以通过"^"符号来定义否定字符类,它匹配所有不包含在方括号内的字符。定义否定字符类时,将一个"^"符号作为左方括号内的第一个字符。正则表达式/[^abc]/匹配的是"a"、"b"、"c"之外的所有字符。字符类可以使用连字符来表示字符范围。要匹配拉丁字母表中的小写字母,可以使用/[a-z]/,要匹配拉丁字母表中任何字母和数字,自使用/[a-z0-9A-Z]/。
由于某些字符类非常常用,因此在JavaScript的正则表达式语法中,使用了这些特殊字符的转义字符来表示它们。例如,\s匹配的是空格字符、制表符和其他的Unicode空白符,\S匹配的是非Unicode空白符的字符。下表列出了这些字符,并且总结了字符类的语法(注意,有些字符类的转义字符只能匹配ASCII字符,还没有扩展到可以处理Unicode字符,但可以通过十六进制表示方法来显示定义Unicode字符类,例如,/[\u0400-\u04FF]/用以匹配所有的Cyrillic字符)。
表:正则表达式的字符类和转义字符(部分)
字符 | 匹配 |
[...] | 方括号内的任意字符 |
[^...] | 不在方括号内的任意字符 |
. | 除换行符和其他Unicode行终止符之外的任意字符 |
\w | 任何ASCII字符组成的单词,等价于[a-zA-Z0-9] |
\W | 任何不是ASCII字符组成的单词,等价于[^a-zA-Z0-9] |
\s | 任何Unicode空白字符 |
\S | 任何非Unicode空白符的字符,注意\w和\S不同 |
\d | 任何ASCII数字,等价于[0-9] |
\D | 除了ASCII数字之外的任何字符,等价于[^0-9] |
[\b] | 退格直接量(特例) |
注意,在方括号之内可以写这些特殊转义字符。比如,由于\s匹配所有的空白字符,\d匹配的是所有的数字,因此/[\s\d]/就匹配任意空白符或者数字。注意,这里有一个特例。下面我们将会看到转义符\b具有的特殊含义,当用在字符类中时,它表示的是退格符,所以要在正则表达式中按照直接量表示一个退格符,只需要使用具有一个元素的字符类/[\b]/。
二、重复
用刚刚学过的正则表达式语法,可以把两位数描述成/\d\d/,四位数描述成/\d\d\d\d/。但到目前为止,还没有一种方法可以用来描述任意多位数字,或者描述由三个字母和一个数字构成的字符串。这些正则表达式语法中较为复杂的模式都提到了正则表达式中某元素的“重复出现次数”。
我们在正则模式之后跟随用以指定字符重复的标记。由于某些重复种类非常常用,因此就有一些专门用于表示这种情况的特殊字符。例如,"+"用以匹配前一个模式的一个或多个副本。下表总结了这些表示重复的正则语法:
字符 | 含义 |
{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。
三、非贪婪的重复
上表中列出的匹配重复字符是尽可能多地匹配,而且允许后续的正则表达式继续匹配。因此,我们称之为“贪婪的”匹配。我们同样可以使用正则表达式进行非贪婪匹配。只须在待匹配的字符后跟随一个问号即可:"??","+?","*?"或"{1,5}?"。比如,正则表达式/a+/可以匹配一个或多个连续的字母a。当使用"aaa"作为匹配字符串时,正则表达式会匹配它的三个字符。但是/a+?/也可以匹配一个或多个连续字母a,但它尽可能少地匹配。我们同样将"aaa"作为匹配的字符串,但后一个模式只能匹配第一个a。
使用非贪婪的匹配模式所得到的结果可能和期望并不一致。考虑一下正则表达式/a+b/,它可以匹配一个或多个a,以及一个b。当使用"aaab"作为匹配字符串时,它会匹配整个字符串。现在再试一下非贪婪匹配的版本/a+?b/,它匹配尽可能少的a和一个b。当用它来匹配"aaab"时,你期望它能匹配一个a和最后一个b。但实际上,这个模式却匹配了整个字符串,和该模式的贪婪匹配一模一样。这是因为正则表达式的模式匹配总会寻找字符串中第一个可能匹配的位置。由于该匹配是从字符串的第一个字符开始的,因此在这里不考虑它的子串中更短的匹配(不好理解,推荐http://blog.csdn.net/lxcnn/article/details/4756030这篇文章)。
四、选择、分组和引用
正则表达式的语法还包括指定选择项、字表达式分组和引用前一子表达式的特殊字符。字符"|"用于分隔供选择的字符。例如,/ab|cd|ef/可以匹配字符串"ab", 也可以匹配字符串"cd",还可以匹配字符串"ef"。/\d{3}|[a-z]{4}/匹配的是三位数字或者四个小写字母。
注意,选择项的尝试匹配次序是从左到右,直到发现了匹配项。如果左边的选择项匹配,就忽略右边的匹配项,即使它产生更好的匹配。因此,当正则表达式/a|ab/匹配字符串"ab"时,它只能匹配第一个字符。
正则表达式中的圆括号有多种作用。一个作用是把单独的项组合成子表达式,以便可以像处理一个独立的单元那样用“|”、“*”、“+”或者“?”等来对单元内的项进行处理。例如,/java(script)?/可以匹配字符串"java",其后可以有"script",也可以没有。/(ab|cd)+|ef/可以匹配字符串“ef”,也可以匹配字符串“ab”或“cd”的一次或多次重复。