8.3 字符匹配
正则表达式的语法主要就是对各个元字符功能的描述。元字符从功能上大致分为模式匹配符、定位符与原义字符、限定符、选择匹配符、特殊字符、字符匹配符、分组组合和反向引用符等。本节先简单介绍字符匹配模式规则。
8.3.1 认识普通字符和元字符
根据正则表达式语法规则,匹配模式是由一系列字符构成的。大多数字符仅能够描述自身,这些字符被称为普通字符,如所有的字母、数字等。
【示例】普通字符只能够匹配字符串中与自身相同的字符。例如,对于字符串“javascript”来说,如果要查找整个字符串,需要设计如下正则表达式:
var r = /javascript/;
正则表达式还规定了一系列的特殊字符,特殊字符不是按照字符自身进行匹配的,但是它们拥有特殊的语义。例如:
(、)、[、]、{、}、\、|、^、$、? 、*、.
特殊字符是不能够匹配自身的。由于具有特殊的语义,所以要匹配自身,还需要进行转义。正是得益于这些特殊的字符,用户才可以设计出很多灵活、实用的正则表达式,并利用这些正则表达式匹配各种复杂的文本信息。因此,这些字符也被称为元字符。
8.3.2 字符直接量
字符的表示方法有多种,除了可以直接使用字符来表示它们本身外,还可以使用ASCII编码或者Unicode编码来表示。
1.ASCII编码
如果使用ASCII编码表示字符,则必须指定一个两位的十六进制值,并在前面增加“\x”前缀。
【示例1】下面使用ASCII编码来定义正则表达式直接量。
var r=/\x61/; //以ASCII编码匹配字母a
由于字母a的ASCII编码为97,被转换为十六进制数值后为61,因此如果要匹配字符a,就应该在前面添加“\x”前缀,以提示该组合为ASCII编码。
var s = "javascript"; var a=s.match(r); //即匹配第一个字符a
【示例2】除了十六进制外,还可以直接使用八进制数值来进行匹配:
var r=/\141/; //141是字母a的ASCII编码的八进制值 var s = "javascript"; var a=s.match(r); //即匹配第1个字符a
使用十六进制需要添加“\x”前缀,主要是避免语义混淆,但是八进制不需要添加前缀。而对于十进制的ASCII编码值是不能够直接使用的。
注意:ASCII编码只能够匹配有限的单字节拉丁字符,对于双字节的汉字等字符是无法表示的。
2.Unicode编码
如果使用Unicode编码表示,则必须指定一个4位的十六进制值,并在前面增加“\u”前缀。
【示例3】下面使用Unicode编码来定义正则表达式直接量。
var r=/\u0061/; //以Unicode编码匹配字母a var s="javascript"; //字符串直接量 var a=s.match(r); //即匹配第一个字符a
【示例4】当这些特殊的转义字符用在RegExp()构造函数中时,应注意使用双斜杠来表示。
var r = new RegExp("\\u0061");
因为,RegExp()构造函数会把“\u”解释为字符u本身,所以对于“\u0061”编码来说,在构造的正则表达式中就成为了“u0061”,从而失去它原来的语义。
除了这些固定的字符和转义编码外,JavaScript还支持一些特殊的字符,如表8-1所示。
表8-1 JavaScript中的特殊字符
对于这些特殊字符,如果在RegExp()构造函数中使用,则必须使用双反斜杠进行转义。对于元字符来说,如果要使用字符本义,则需要添加反斜杠前缀进行转义。例如:
\(、\)、\ [、\]、\{、\}、\\、\|、\^、\$、\? 、\*、\.
表示的语义分别是这些元字符本身,此时它们就不再拥有特殊的语义:
(、)、[、]、{、}、\、|、^、$、? 、*、.
8.3.3 简单字符类
所谓字符类,就是把多个单独的字符放在中括号内以构成一个字符集合。字符类能够与任何包含的字符进行匹配。
【示例1】下面的代码定义了一个简单的字符类正则表达式直接量。
var r=/[abc]/gi; //定义字符类 var s="javascript"; //字符串直接量 var a=s.match(r); //返回数组["a", "a", "c"]
正则表达式/[abc]/gi是一个简单的字符类,它包含3个字符直接量:a、b、c。这样在字符串中只要包含字符类中任意一个字母,都被视为匹配的意思。也就是说,字符类相当于JavaScript的逻辑或运算,中括号相当于逻辑或运算符(||)。例如:
[abc]
可以理解为:
a || b || c
该字符类可以匹配字符“a”、“b”或“c”,但是不能够匹配字符“ab”、“bc”、“ab”或“abc”。
【示例2】对于下面这个字符串,如果匹配所有单词(共计5个),使用简单的字符类就能够很轻松地解决:
var s = "abc abd abe abf abg";
简单分析该字符串,每个词都包含有相同的子字符串“ab”,这样可以把每个词的第三个字符存放在中括号内,构成一个字符类,这样对于任何一个单词都可以匹配:
var r=/ab[cdefg]/g; //通过简单的字符类实现匹配多个单词 var a=s.match(r); //返回数组["abc", abd", abe", abf", "abg"]
【示例3】在字符类中(即中括号内)可以使用特殊字符。对于上面正则表达式也可以这样设计:
var s = "abc abd abe abf abg"; var r = /ab[\u0063\u0064\u0065\u0066\u0067]/g; //通过字符的Unicode十六进制值来表示 var a=s.match(r); //返回数组["abc", abd", abe", abf", "abg"]
它们将会实现相同的匹配效果。
8.3.4 反义字符类
反义字符就是除了字符类中指定的字符以外任意的字符。如果在字符类内部添加脱字符(^)前缀,即定义了反义字符类。
【示例1】针对上面正则表达式/ab[cdefg]/g;,也可以这样设计:
var s = "abc abd abe abf abg"; var r=/ab[^ab]/g; //通过反义字符类设计匹配模式 var a=s.match(r); //返回数组["abc", abd", abe", abf", "abg"]
在上面的匹配模式中,单词的第三个字符将匹配除了a和b以外的任意字符。通过反义字符类,可以设计使用有限字符来匹配无限字符的意图。
【示例2】在字符处理中,需要匹配的字符无法预料,或者难以通过简单字符类进行一一枚举,那么可以分析该匹配可能不会包含的字符,然后取反义,以反义字符类实现以少应多的目的。
var r = /[^0123456789]/g;
在这个正则表达式直接量中,将会匹配除了数字以外任意的字符。反义字符类比简单字符类显得功能更加强大和实用。
8.3.5 字符范围类
所谓范围类,就是指定字符列表时,仅指定起止字符,然后中间部分通过连字符(-)来表示,从而达到简化表达式的目的。
【示例1】下面的代码定义了简单的字符范围类正则表达式直接量。
如果匹配所有数字,可以这样设计:
var r = /[0123456789]/g;
如果匹配所有字母,也可以这样设计:
var r = /[abcdefghijklmnopqrstuvwxyz]/gi;
此时使用字符范围类进行匹配。
var r=/[0-9]/g; //等价于/[^0123456789]/g var r=/[a-z]/g; //等价于/[abcdefghijklmnopqrstuvwxyz]/g
可以把它理解为从数字0~9这个范围的任意数字匹配,或者是从字母a~z之间任意字母匹配。
【示例2】也可以在字符范围类中使用字符编码。
字符范围类是遵循字符编码的顺序来进行匹配的。如果所要测试的字符恰好是按照字符编码的排列顺序时,就可以使用这种表达式来表示。
如果匹配任意ASCII字符,可以设计:
var r = /[\u0000-\u00ff]/g;
如果匹配任意双字节的汉字,可以设计:
var r = /[^\u0000-\u00ff]/g;
对于数字来说,除了直接使用数字字符来表示外,还可以使用Unicode编码实现:
var r = /[\u0030-\u0039]/g;
使用如下正则表达式可以匹配任意大写字母:
var r = /[\u0041-\u004A]/g;
使用如下正则表达式来匹配任意小写字母:
var r = /[\u0061-\u007A]/g;
【示例3】也可以在字符类中混合使用简单字符、范围和反义等类型:
var s="abcdez"; //字符串直接量 var r=/[abce-z]/g; //字符类包含字符a、b、c,以及从e~z之间的任意字符 var a=s.match(r); //返回数组["a", "b", "c", "e", "z"]
【示例4】在字符类内部不要有空格,否则会被认为是一个空格进行匹配。
var r = /[0-9 ]/g;
上面正则表达式不仅匹配所有数字,还会匹配所有空格。
【示例5】如果要匹配任意大小写字母和数字,则可以使用:
var r = /[a-zA-Z0-9]/g;
【示例6】可以在一个正则表达式中设计多个字符类,从而设计更灵活的匹配。
var s="abc4 abd6 abe3 abf1 abg7"; //字符串直接量 var r=/ab[c-g][1-7]/g; //匹配第一、二个字符为ab,第三个字符为从c到g,第四个字符为1~7的任意数字 var a=s.match(r); //返回数组["abc4", "abd6", "abe3", "abf1", "abg7"]
8.3.6 预定义字符类
由于某些字符类比较常用,所以JavaScript的正则表达式语法就包含了一些特殊字符和转义序列来表示这些常用的类。例如,\s匹配空格符、制表符和其他Unicode空白符,\S匹配的是非Unicode空白符。预定义字符类的详细说明如表8-2所示。
表8-2 预定义字符类
注意:有些字符类转义序列只匹配ASCII字符,还没有扩展到可以处理Unicode字符。用户可以自定义Unicode字符类。由于在方括号内也可以使用这些特殊的字符类转义序列,因此可以使用/[\u0F00-\u0FFF]匹配所有的藏文字符。
【示例】下面的代码比较了字符范围类和预定义字符类的使用。
使用预定义字符类可以简化正则表达式的表示模式。例如,匹配3个字母,如果不使用\w,则需要这样来设计:
var r = /[a-zA-Z0-9][a-zA-Z0-9][a-zA-Z0-9]/g;
但是如果使用预定义字符类,正则表达式就会变得简单许多。例如,上面正则表达式可以这样来描述:
var r = /\w\w\w/g;
另外,转义序列\b具有特殊的语义。当它用在字符类中时表示退格符,但是要在正则表达式中以直接量形式表示一个退格符,只需要使用具有一个元素的字符类(即/[\b]/)来表示。