JavaScript 中的正则表达式

前言

正则表达式(Regular Expression) 是一个描述字符模式的对象,它被用来匹配字符串中的字符组合。 JavaScript 中的 String 类和 RegExp 类都定义了使用正则表达式的方法。

正则表达式的定义

JavaScript 中的正则表达式用 RegExp 对象表示,所以我们可以使用 RegExp() 构造函数来创建 RegExp 对象,不过在实际 使用中 RegExp 对象更多的是通过直接量语法来创建的。正则表达式直接量定义为包含在一对斜杠 "/" 之间的字符。

1
2
var expression1 = /pattern/flags; //RegExp直接量
var expression2 = new RegExp("pattern", "flags"); //RegExp构造函数

flags 代表修饰符,本文稍后有详细介绍。

直接量字符

正则表达式中的所有字母和数字都是按照字面含义进行匹配的,但它也支持通过反斜线 "\" 转义的非字母的字符匹配,这些转义字符如下表所示:

字符 匹配
字母和数字字符 自身
\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

字符类

将直接量字符单独放进方括号内就组成了字符类,一个字符类可以匹配它所包含的任意字符。例如,正则表达式 /[abc]/ 就和字母 "a", "b", "c" 中的任意一个都匹配。正则表达式的字符类也支持转义字符定义的特殊字符,如下表所示:

字符类

重复

正则表达式中还有一些重复字符语法,如下表所示:

重复

举一些栗子:

1
2
3
4
/\d{2,4}/ //匹配2~4个数字
/\w{3}\d?/ //精确匹配三个单词字符和一个可选的数字
/\s+java\s+/ //匹配前后带有一个或多个空格的字符串"java"
/[^(]*/ //匹配一个或多个非左括号的字符

选择与分组

字符 "|" 用于分隔供选择的字符。例如, /ab|cd|ef/ 可以匹配字符串 "ab", "cd", "ef"/\d{3}|[a-z]{4}/ 匹配三位数字或四个小写字母。

注意,选择项的尝试匹配次序是从左到右,直到发现匹配项。如果左边的选择项匹配,就忽略右边的匹配项,即使它产生更好的匹配。例如,当 /a|ab/ 匹配字符串 "ab" 时,它只能匹配第一个字符。

正则表达式的圆括号作用是把单独的项组合成子表达式,还可以在完整的模式中定义子模式。例如,/java(script)?/ 可以匹配字符串 "java" ,其后可以有 "script" 也可以没有。

指定匹配位置

在正则表达式中,有一些锚字符,用来指定字符串中的特殊位置,如下表所示:

特殊位置

修饰符

正则表达式还有3个修饰符,分别是 "i", "g", "m" 。它们放在字面量的 "/" 符号之外,是 RegExp() 构造函数的第二个参数。它们的含义如下表所示:

修饰符

用于模式匹配的 String 方法

search() 方法

search() 方法的参数是一个正则表达式,返回第一个与之匹配的子串的起始位置,若无匹配的子串,则返回 -1。

1
"JavaScript".search(/script/i); //将返回4

若 search() 方法的参数不是正则表达式,则首先会通过 RegExp() 构造函数将它转换为正则表达式。

search() 方法不支持全局搜索,因为它会忽略正则表达式参数中的修饰符 g

replace() 方法

replace() 方法用以执行检索与替换操作,它的第一个参数是正则表达式,第二个参数是要替换的字符串。它支持全局替换,不会忽略 g 修饰符。

1
text.replace(/javascript/gi, "JavaScript"); //将text中所有不区分大小写的javascript都替换为JavaScript

若 replace() 方法第一个参数是字符串而不是正则表达式,则它会直接搜索这个字符串,不会像 search() 方法那样通过 RegExp() 构造函数转换为正则表达式。

match() 方法

match() 方法是最常用的 String 正则表达式方法,它的参数是一个正则表达式(若不是正则表达式会通过 RegExp() 构造函数转换),返回的是一个由匹配结果组成的数组。

若参数的正则表达式设置了 g 修饰符,则该方法返回的是一个包含所有匹配结果的数组。例如:

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

若参数的正则表达式没有设置 g 修饰符,它不会进行全局搜索,但也返回一个数组,数组第一个元素是匹配的字符串,余下的元素是正则表达式中用圆括号括起来的子表达式匹配的字符串。例如:

1
2
3
4
5
6
7
8
9
var url = /(\w+):\/\/([\w.]+)/;
var text = "Visit my blog at http://songziming.com.cn";
var result = text.match(url);
/* ["http://songziming.com.cn",
"http",
"songziming.com.cn",
"~szm"
]
*/

split() 方法

split() 用来分隔字符串为数组,它的参数既能是一个字符串也能是一个正则表达式(字符串不会通过 RegExp() 构造函数转换为正则表达式)。例如:

1
2
"123,456,789".split(","); //返回["123", "456", "789"]
"1, 2, 3, 4".split(/\s*,\s*/); //返回["1", "2", "3", "4"]

RegExp 对象

文章开头讲到过,正则表达式还能通过 RegExp() 构造函数创建,它带有两个参数,第一个参数是正则表达式的主体部分,第二个参数可选,是修饰符。不过需要注意的是,在主体部分,不论是字符串直接量还是正则表达式,都需要使用 “\” 字符作为转义字符的前缀,因此当给 RegExp() 传入一个字符串表述的正则表达式时,必须将 “\” 替换为 “\“ 。例如:

1
var zipcode = new RegExp("\\d{5}", "g"); //全局匹配字符串中的5个数字

RegExp 对象的属性

每个 RegExp 对象都具有5个属性,如下图所示:

RegExp对象属性

其中,global, ignoreCase, multiline 都是只读的布尔值,source 是一个只读的字符串,lastIndex 是一个可读可写的整数。

RegExp 对象的方法

exec() 方法

exec() 是 RegExp 对象最主要的模式匹配方法,它与 String 的 match() 方法非常相似。

exec() 方法的参数是一个字符串,它执行对这个字符串的匹配搜索。如果没有找到任何匹配,就返回 null;如果找到了一个匹配,它将返回一个数组,就像 match() 方法为非全局检索返回的数组一样。数组第一个元素与正则表达式匹配的字符串,余下的元素是与圆括号内的子表达式匹配的子串。返回的数组还包括了 index 与 input 两个属性,index 属性包含了发生匹配的字符位置,input 属性引用的是正在检索的字符串。

不管正则表达式是否具有g修饰符,exec() 方法都返回一样的数组。当同一个正则表达式第二次调用 exec() 方法时,将从 lastIndex 的位置开始检索。若 exec() 没有发现任何匹配结果,则将 lastIndex 重置为0。例如:

1
2
3
4
5
6
7
var pattern = /Java/g;
var text = "JavaScript is more fun than Java!";
var result;
while((result = pattern.exec(text)) !== null) {
alert("Matched '" + result[0] + "'" +
" at position " + result.index +
"; next search begins at " + pattern.lastIndex);

test() 方法

test() 方法的参数是一个字符串,返回一个指示是否匹配的布尔值。例如:

1
2
var pattern = /java/i;
pattern.test("JavaScript"); //返回true

调用 test() 与 exec() 等价。当一个全局正则表达式调用 test() 时,它会从 lastIndex 的位置开始检索,若找到了匹配结果,那么它就会设置 lastIndex 为当前匹配子串的结束位置。

由于这个特性的存在,如果让一个带有 g 修饰符的正则表达式对多个字符串执行 exec() 或 test() 时,要么在每个字符串中找出所有的匹配以便将 lastIndex 自动重置为 0,要么需要手动重置 lastIndex 为 0。否则,下一次对新字符串进行检索时,起始位置可能就不是字符串的开始位置了。

在 ECMAScript 5 中,正则表达式直接量每次计算都会创建一个新的 RegExp 对象,因此每次对新字符串检索时 lastIndex 都是0。

------ 本文结束 ------
坚持原创技术分享,您的支持将鼓励我继续创作!