JavaScript从入门到精通(第3版)
上QQ阅读APP看书,第一时间看更新

7.2 正则表达式语法

正则表达式的语法主要就是对各个元字符功能的描述。元字符从功能上大致分为模式匹配符、定位符与原义字符、限定符、选择匹配符、特殊字符、字符匹配符、分组组合和反向引用符。本节将对各种元字符进行详细讲解。

7.2.1 模式匹配符

表7.1列出了在正则表达式中能够使用的字符列表及相关描述。

表7.1 模式匹配符

续表

7.2.2 定位符与原义字符

在进行数据验证时,可以使用一些定位符来限定字符出现的位置,以方便匹配。同时,对于表达式中的元字符,需要进行转义,使其变成原义字符,才能正常显示出来。下面详细介绍这两种字符。

(1)文本验证定位符

定位符用于规定匹配模式在目标字符串中出现的位置,例如,规定匹配模式只能出现在开头或结尾处,这样对文本格式的验证非常有用。

在正则表达式中,有以下几个用于验证文本的定位符。

用“^”匹配目标字符串的开始位置

匹配必须发生在目标字符串的开头处,“^”必须出现在表达式的最前面,才具有定位符作用。

例如,“^o”与“ok”中的“o”匹配,但与“no”中的“o”不匹配。如果设置了RegExp对象实例的multiline属性,“^”还会与行首匹配,即与“\n”或“\r”之后的位置匹配。

【例7.1】 匹配字符“^”的使用。(实例位置:资源包\TM\sl\7\01)

运行程序,在浏览器窗口中会显示匹配成功的相关内容,如图7.1所示。

图7.1 匹配字符“^”的使用

代码如下:

用“$”匹配目标字符串的结尾位置

匹配必须发生在目标字符串的结尾处,“$”必须出现在表达式的最后面,才具有定位符作用。

例如,“o$”与“no”中的“o”匹配,但与“ok”中的“o”不匹配。如果设置了RegExp对象实例的mutiline属性,“$”还会与行末匹配,即与“\n”“\r”之前的位置匹配。

【例7.2】 匹配字符“$”的使用。(实例位置:资源包\TM\sl\7\02)

运行程序,在浏览器窗口中会显示匹配成功的相关内容,如图7.2所示。

图7.2 匹配字符“$”的使用

代码如下:

用“\b”匹配一个字边界

“\b”包含了字符与空格间的位置,以及目标字符串的开始和结束位置等。

例如,“er\b”匹配“order to”中的“er”,但不匹配“verb”中的“er”。

【例7.3】 匹配字符“\b”的使用。(实例位置:资源包\TM\sl\7\03)

运行程序,在浏览器窗口中会显示匹配成功的相关内容,如图7.3所示。

图7.3 匹配字符“\b”的使用

代码如下:

用“\B”匹配非字边界

例如,“er\B”匹配“verb”中的“er”,但不匹配“order”中的“er”。

(2)特殊字符转义

在表达式中用到的一些元字符不再表示原来的字面意义。如果要匹配这些有特殊意义的元字符,必须使用“\”将这些字符转义为原义字符。需要进行转义的字符有“$”“(”“)”“*”“+”“.”“[”“]”“?”“\”“/”“^”“{”“}”和“|”。

“\”的作用是将下一字符标记为特殊字符、原义字符、反向引用或八进制转义符,所以,要匹配字面意义的“\”,需要使用“\\”表示。

7.2.3 限定符与选择匹配符

(1)限定符

用“+”限定必须出现一次或连续多次

“+”元字符规定其前导字符必须在目标对象中出现一次或连续多次。

如“/fo+/”,因为上述正则表达式中包含“+”元字符,表示可以与目标对象中的“fool”、“fo”或者“football”等在字母“f”后面连续出现一个或多个字母“o”的字符串相匹配,但与一个单独的“f”不匹配。“+”等效于“{1,}”。

用“*”限定可以出现的次数

“*”元字符规定其前导字符必须在目标对象中出现零次或连续多次。

如“/eg*/”,因为上述正则表达式中包含“*”元字符,表示可以与目标对象中的“easy”、“ego”或者“egg”等在字母“e”后面连续出现0个或多个字母“g”的字符串相匹配。“*”等效于“{0,}”。

用“?”限定最多出现一次

“?”元字符规定其前导字符必须在目标对象中出现0次或1次。

如“/Wil?/”,因为上述正则表达式中包含“?”元字符,表示可以与目标对象中的“Win”或者“Wilson”等在字母“i”后面连续出现0个或1个字母“l”的字符串相匹配,但不能匹配“Will”。“?”等效于“{0,1}”。

用“{n}”限定连续出现的次数

规定前面的元素或组合项连续出现n次。n为非负整数。

例如,“o{2}”不能与“job”中的“o”匹配,但可以与“book”中的两个“o”匹配,也可以与“booook”中的任意两个连续的“o”匹配。

用“{n,}”限定至少出现的次数

规定前面的元素或组合项至少连续出现n次。n为非负整数。

例如,“o{2,}”不能与“job”中的“o”匹配,但可以与“booook”中的所有“o”匹配。

用“{n,m}”限定最少与最多出现的次数

规定前面的元素或组合项至少连续出现n次,最多连续出现m次。m和n是非负整数,其中n≤m,逗号和数字之间不能有空格。

例如,“o{1,3}”既可匹配“book”中的两个“o”,又可匹配“booook”中3个连续的“o”。

(2)贪婪匹配与非贪婪匹配

默认情况下,正则表达式使用最长匹配原则,即贪婪匹配原则。

例如,要将“book”中匹配“bo?”的部分替换成“l”,替换后的结果是“lok”,而不是“look”;如果要将“book”中匹配“bo*”的部分替换成“l”,替换后的结果是“lk”,而不是“lok”或“look”。

当解释器将代码中的字符解析成一个个的编译器,并在处理代码当前最小语法单元时,编译器会使用一种贪婪匹配算法。也就是说,会尽可能让一个单元包含更多的字符。如果当字符“?”紧跟任何其他限定符(“*”“+”“?”“{n}”“{n,}”“{n,m}”)之后时,匹配模式变成使用最短匹配原则,即非贪婪匹配原则。例如,在字符串“booook”中,“bo+?”只匹配“bo”部分,而“bo+”匹配“boooo”部分。

(3)选择匹配符

选择匹配符“|”,用于选择匹配两个选项之中的任意一个,其两个选项是“|”字符两边尽可能最大的表达式。

例如,“abcd|efgh1”匹配的是“abcd”或“efgh”,而不是“abcd1”或“efgh1”;如果要匹配“abcd1”或“efgh1”,应该使用括号创建子表达式,即“(abcd|efgh) 1”。

7.2.4 字符匹配符

字符匹配符用于指定可以匹配多个字符中的任意一个。

“[…]”匹配方括号中包含的字符集中的任意一个字符。

例如,“[abc]”可以与“a”“b”“c”3个字符中的任何一个匹配。如果字符集中要包含“]”字符,需将其放在第一位,即紧跟在“[”后面。“[]”中的字符“\”仍作为转义字符,若要在“[…]”中包含“\”字符本身,需使用“\\”。

“[^…]”匹配方括号中未包含的任意字符。

例如,“[^abc]”可匹配“a”“b”“c”3个字符之外的任何字符。只要字符“^”不是出现在第一个“[”后面,就还是字面意义上的“^”。

“[a-z]”匹配指定范围内的任何字符。

例如,“[1-9]”匹配1~9的任何数字字符。若要在方括号中包含字面意义的连字符“-”,可以用“\”将其标记为原义字符,例如“[a\-z]”。也可以将“-”放在方括号的开始或结尾处,例如,“[-a-z]”或“[a-z-]”匹配所有小写字母和连字符。

“[^a-z]”匹配不在指定范围内的任何字符。

例如,“[^a-z]”匹配不在a~z之间的任何字符。

【例7.4】 字符匹配的使用。(实例位置:资源包\TM\sl\7\04)

运行程序,在浏览器窗口中会显示匹配成功的相关内容,如图7.4所示。

图7.4 字符匹配

代码如下:

“\w”匹配任何单字字符,即英文字母或者数字类字符以及下画线,等效于“[A-Za-z0-9_]”。

“\W”匹配任何非单字字符,即非英文字母或者数字类字符,但不包括下画线。“\W”是“\w”的逆运算,等效于“[^A-Za-z0-9_]”。

“\s”匹配任何空白字符,包括空格、制表符、回车符、换行符等,等效于“[\f\n\r\t\v]”。

“\S”匹配任何非空白字符,是“\s”的逆运算,等效于“[^\f\n\r\t\v]”。

“\d”匹配任何一个数字字符,等效于“[0-9]”。

“\D”匹配任何一个非数字字符,是“\d”的逆运算,等效于“[^0-9]”。

“.”匹配除“\n”之外的任何单个字符。

“()”标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用“\(”和“\)”。

“(.)\l”匹配除“\n”之外的两个连续的相同字符。若要匹配包括“\n”在内的任意字符,可以使用“[\s\S]”、“[\d\D]”或“[\w\W]”等模式。若要匹配“.”字符本身,需要使用“\.”。

【例7.5】 圆点(.)元字符的使用。(实例位置:资源包\TM\sl\7\05)

运行结果如图7.5所示。

图7.5 圆点(.)元字符的使用

代码如下:

7.2.5 分组组合与反向引用符

分组组合符是将表达式中某部分内容组合起来的符号,反向引用符则是用于匹配分组组合捕获到的内容的标识符。

(1)分组组合

“(pattern)”将pattern部分组合成一个可统一操作的组合项和子匹配,每个捕获的子匹配项按照出现的顺序存储在缓冲区中。缓冲区编号从1开始,最多可存储99个子匹配捕获的内容。存储在缓冲区中的子匹配捕获的内容,可以在编程语言中被检索,也可以在正则表达式中被反向引用。若要匹配字面意义的括号字符“(”和“)”,在正则表达式中要分别使用“\(”和“\)”。

(2)反向引用

“\num”匹配编号为num的缓冲区所保存的内容,num是标识特定缓冲区的一位或两位十进制正整数,这种方式称为子匹配的反向引用。反向引用能提供表示相同匹配项的能力。

(3)非捕获匹配

“(?:pattern)”匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。它是将pattern部分组合成一个可统一操作的组合项,但不把这部分内容当作子匹配捕获,即pattern部分是一个非捕获匹配,匹配的内容不存储在缓冲区中供以后使用。这对必须进行组合,但又不想让组合的部分具有子匹配特点的情况很有用。

例如,要将“abc?”中的“abc”组合起来,但并不想将匹配的内容保存在缓冲区中,应该使用“(?:abc)?”,而不能使用“(abc)?”。又如,不能将“industry|industries”简单改写为“industry(ylies)”,若不需要引用或检索括号中的表达式所匹配的结果,最好还是写成“industry(?:ylies)”。

(4)正向“预测先行”匹配

“(?=pattern)”称为正向“预测先行”匹配,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。在被搜索字符串的相应位置必须有pattern部分匹配的内容,但不作为匹配结果处理,更不会存储在捕获缓冲区中供以后使用。

例如,“Windows (?=NT|2000)”只与“Windows 2000”或“Windows NT”中的“Windows”匹配,而不与“Windows 2003”中的“Windows”匹配。

注意

该模式下匹配的结果只是“Windows”部分,而使用“Windows (?:NT|2000)”匹配的是整个“Windows 2000”或“Windows NT”。如果要将“NT”和“2000”前面的“Windows”替换成“Win”,需要使用“Windows (?=NT|2000)”,而不能使用“Windows (?:NT|2000)”;否则,整个“Windows 2000”或“Windows NT”将被替换成“Win”。

(5)反向“预测先行”匹配

“(?!pattern)”称为反向“预测先行”匹配,在被搜索字符串的相应位置不能有pattern部分匹配的内容,此外,其功能与正向“预测先行”匹配一样。

例如,“Windows (?!NT|2000)”不与“Windows 2000”或“Windows NT”中的“Windows”匹配,但可以与“Windows 2003”中的“Windows”匹配。

7.2.6 实际应用

正则表达式主要用于字符串处理、表单验证等。下面介绍一些常用的正则表达式。

(1)普通字符匹配

下面是一些在Web编程中经常会遇到的字符匹配模式。

匹配中文字符的正则表达式。

    [\u4e00-\u9fa5]

匹配双字节字符(包括汉字在内)的正则表达式。

    [^x00-xff]

匹配空行的正则表达式。

    n[s|]*r

匹配HTML标记的正则表达式。

    /<(.*)>.*</1>|<(.*) />/

匹配首尾空格的正则表达式。

    (^s*)|(s*$)

计算字符串长度(一个双字节字符长度计2,ASCII字符计1)的正则表达式。

    String.prototype.len=function(){return this.replace([^x00-xff]/g,"aa").length;}

匹配网址URL的正则表达式。

    http://([w-]+.)+[w-]+(/[w- ./?%&=]*)?

匹配E-mail地址的正则表达式。

    w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*

(2)限制表单所输入的内容

有时需要限制网页表单文本框中输入的内容。下面是一些利用正则表达式来实现这种功能的实例。

用正则表达式限制只能输入中文。

    onkeyup="value=value.replace(/[^u4E00-u9FA5]/g,'')"
    onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^u4E00-u9FA5]/g,''))"

用正则表达式限制只能输入数字。

    onkeyup="value=value.replace(/[^d]/g,'')
    "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^d]/g,''))"

用正则表达式限制只能输入数字和英文。

    onkeyup="value=value.replace(/[W]/g,'')
    "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^d]/g,''))"

用正则表达式限制只能输入全角字符。

    onkeyup="value=value.replace(/[^uFF00-uFFFF]/g,'')"
    onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^uFF00-uFFFF]/g,''))"

(3)去掉重复的字符

利用正则表达式可以去掉字符串中重复的字符。

例如,下面的例子利用正则表达式去掉了字符串中重复的字符。首先应用正则表达式取出重复的字符,再以重复的字符建立第二个表达式,取到不重复的字符,最后两者串联,代码如下:

    var s="abacabefgeeii"
    var s1=s.replace(/(.).*\1/g,"$1")
    var re=new RegExp("["+s1+"]","g")
    var s2=s.replace(re,"")
    alert(s1+s2);

运行结果:

    abeicfg

说明

这种方法对于字符顺序有要求的字符串可能不适用。