第11章 使用正则表达式的模式匹配(二)

来源:互联网 发布:知乎snh48 编辑:程序博客网 时间:2024/05/01 20:58

11.2 用于模式匹配的 String 方法

迄今为止,虽然本章已经讨论过了用于创建正则表达式的语法,但是我们还没有检验过这些正则表达式在 JavaScript 代码中如何使用。在这一节中,我们将讨论 String 对象的部分方法,它们在正则表达式中执行模式匹配和检索与替换操作。在此后的小节中,我们将继续讨论使用 JavaScript 正则表达式的模式匹配,不过讨论的是 RegExp 对象和它的方法及属性。注意,接下来的讨论只是与正则表达式相关的各种方法和属性的概述。

类 String 支持四种利用正则表达式的方法。

(1)最简单的是 search() 。该方法以正则表达式为参数,返回第一个与之匹配的子串的开始字符的位置,如果没有任何匹配的子串,它将返回 -1 。例如,下面的调用返回的值为 4:

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

如果 search() 的参数不是正则表达式,它首先将被传递给 RegExp 构造函数,转换成正则表达式。search() 不支持全局检索,因为它忽略了正则表达式参数的标志 g 。

(2)方法 replace() 执行检索与替换操作。它的第一个参数是一个正则表达式,第二个参数是要进行替换的字符串。它将检索调用它的字符串,根据指定的模式来匹配。如果正则表达式中设置了标志 g ,该方法将用替换字符串替换被检索的字符串中所有与模式匹配的子串,否则它只替换所发现的第一个与模式匹配的子串。如果 replace() 的一个参数是字符串,而不是正则表达式,该方法将直接检索那个字符串,而不是像 search() 那样用 RegExp() 构造函数将它转换成一个正则表达式。例如,我们可以用如下方法使用 replace() 将文本字符串中的所有 javascript (不区分大小写) 统一为 "JavaScript" :

text.replace(/javascript/gi, "JavaScript");

但是 replace() 的功能远比上例所示强大。回忆一下可以知道,正则表达式中用括号括起来的子表达式是从左到右进行编号的,而且正则表达式会记住与每个子表达式匹配的文本。如果在替换字符串中出现了符号 $ 加数字,那么 replace() 将用与指定的子表达式相匹配的文本来替换这两个字符这是一个非常有用的特性。例如,我们可以用它将一个字符串中的直接引号替换为大引号,这与 ASCII 字符相似:




最值得注意的是,replace() 方法的第二个参数可以是函数,该函数能够动态地计算替换字符串。

(3)方法 match() 是最常用的 String 正则表达式方法。它唯一的参数就是一个正则表达式 (或把它的参数传递给构造函数 RegExp() 以转换成正则表达式),返回的是包含了匹配结果的数组。如果该正则表达式设置了标志 g ,该方法返回的数组包含的就是出现在字符串中的所有匹配。例如:

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

如果该正则表达式没有设置标志 g ,match() 进行的就不是全局性检索,它只是检索第一个匹配。但即使 match() 执行的不是全局检索,它也返回一个数组。在这种情况下,数组的第一个元素就是匹配的字符串,而余下的元素则是正则表达式中用括号括起来的子表达式。因此,如果 match() 返回了一个数组 a ,那么 a[0] 存放的是完整的匹配,a[1] 存放的则是与第一个用括号括起来的表达式匹配的子串,以此类推。为了和方法 replace() 保持一致, a[n] 存放的是 $n 的内容。

例如,使用如下的代码来解析一个 URL :


最后,还应该了解 match() 的一个特性。它返回的数组和其他的数组一样具有一个 length 属性。如果 match() 是作用于一个非全局的正则表达式,那么它返回的数组还包含另外两个属性 -- index 和 input ,前者包含的是在字符串中匹配开始处的字符的位置,后者则是目标字符串的一个副本。这样,在上面的代码中,result.index 属性的值应该等于 17 ,因为匹配了的 URL 是从文本中的第 17 个字符的位置开始。而属性 result.input 则应该具有和变量 text 相同的字符串。对于没有标志  g 的正则表达式 r 和字符串 s ,调用 s.match(r) 返回的值与 r.exec(s) 相同。

(4)String 对象的最后一个有关正则表达式的方法是 split() 。这个方法可以把调用它的字符串分解为一个子串数组,使用的分隔符是它的参数。例如:

"123,456,789".split(",") ;                    // Returns ["123", "456", "789"]

split() 方法也可以以正则表达式为参数。这种能力使该方法更强大。例如,我们可以指定分隔符,允许两边有任意多个空白符:

"1,2,  3 ,     4    ,5".split(/\s*,\s*/);        // Returns["1","2","3","4","5"]

11.3 RegExp 对象

正则表达式使用 RegExp 对象来表示的。除了构造函数 RegExp() 之外,RegExp 对象还支持三种方法和大量的属性。RegExp 类的一个不寻常的特性是它既定义了类 (静态) 属性,又定义了实例属性。也就是说,它既定义了属于构造函数 RegExp() 的全局属性,又定义了其他属于单独的 RegExp 对象的属性。我们将在接下来的两节中介绍 RegExp 的模式匹配方法和属性。

构造函数 RegExp() 有一个或两个字符串参数,它将创建一个新的 RegExp 对象。该构造函数的第一个参数是包含正则表达式主体的字符串,即正则表达式直接量中出现在斜线对之间的文本。注意,无论是字符串直接量还是正则表达式都是用了字符 \ 表示转义序列,所以当将正则表达式作为字符串直接传递给 RegExp() 时,必须用 \\ 替换所有 \ 字符。RegExp() 的第二个参数是可选的。如果提供了这个参数,它说明的就是该正则表达式的标志。它应该是 "g" 、"i"、 "m" 或它们的组合。例如:

var zipcode = new RegExp("\\d{5}", "g");

当要动态创建一个正则表达式,而不能用正则表达式直接量的语法来表示时,构造函数 RegExp() 非常有用。例如,如果检索的字符串是由用户输入的,那么就必须在运行时用 RegExp() 构造函数来创建正则表达式。

11.3.1 用于模式匹配的 RegExp 方法

RegExp() 对象定义了两个用于执行模式匹配操作的方法。它们的行为和前面介绍过的 String 方法很相似。主要的 RegExp 模式匹配方法是 exec() 。它与之前介绍过的 String 方法 match() 相似,只不过它是以字符串为参数的 RegExp 方法,而不是以  RegExp 对象为参数的 String 方法。exec() 方法对一个指定的字符串执行一个正则表达式,简而言之,就是在一个字符串中检索匹配。如果没有找到任何匹配,它将返回  null ,但是,如果它找到了一个匹配,将返回一个数组,就像方法 match() 为非全局检索返回的数组一样。这个数组的元素 0 包含的是与正则表达式相匹配的字符串,余下的所有元素包含的是与括号括起来的子表达式相匹配的子串。而且,属性 index 包含了匹配发生的字符的位置,属性 input 引用的是被检索的字符串。

和 match() 方法不同的是,exec() 返回的数组类型相同,无论该正则表达式是否具有全局标记 g 。回忆一下,当传递给方法 match() 的是一个全局正则表达式时,它返回的是一个匹配的数组。相比之下,方法 exec() 返回的总是一个匹配,而且提供关于该匹配的完整信息。当一个具有 g 标志的正则表达式调用 exec() 时,它将把该对象的 lastIndex 属性设置到紧接着匹配子串的字符位置。当一个正则表达式第二次调用 exec() 时,它将从 lastIndex 属性所指示的字符处开始检索。如果 exec() 没有发现任何匹配,它会将 lastIndex 属性重置为 0 (在任何时候都可以将 lastIndex 属性设为 0 ,每当在一个字符串中找到最后一个匹配并开始用同一个 RegExp 对象来检索另一个字符串时,退出本次检索应该这样做)。这一特殊的行为使得可以反复调用 exec() 遍历一个字符串中所有匹配的正则表达式。例如:

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);

}

另一个 RegExp 方法是 test() ,它比 exec() 方法简单一些。它的参数是一个字符串,如果这个字符串包含正则表达式的一个匹配,它就返回 true :

var pattern = /java/;

pattern.test("JavaScript");         // Returns true

调用 test() 方法等价于调用 exec() 方法,如果 exec() 的返回值不是 null,它将返回 true 。由于这种等价性,当一个全局正则表达式调用方法 test() 时,它的行为和方法 exec() 相同,即它从 lastIndex 指定的位置处开始检索特定的字符串,如果它发现了匹配,就将 lastIndex 设置为紧接在那个匹配之后的字符的位置。这样一来,我们就可以使用方法 test() 来遍历字符串,就像用 exec() 方法那样。

String 方法 search()、replace() 和 match() 都没有像 exec() 和 test() 那样使用属性 lastIndex 。事实上,String 方法只是将 lastIndex() 重置为 0 。如果使用一个设置了 g 标记模式的 exec() 方法或 test() 方法来检索多个字符串,那么就必须找到每个字符串中的所有匹配,以便 lastIndex 属性会被自动重置为 0 (这在最后的检索失败时会发生),或者必须明确地将 lastIndex 属性设为 0 。如果忘记了这样做,那么再检索一个新字符串时,起始位置可能就是原来的字符串中的任意位置,而不一定是开头。最后要记住,只有带 g 标志的正则表达式才会发生这种特殊的 lastIndex 行为。如果 RegExp 对象没有标志 g ,exec() 和 test() 将忽略它的 lastIndex 属性。

11.3.2 RegExp 的实例属性

每个 RegExp 对象都有五个属性。属性 source 是一个只读字符串,它存放的是正则表达式的文本。属性 global 是一个只读的布尔值,它说明了该正则表达式是否具有标志 g 。属性 ignoreCase 也是一个只读的布尔值,它说明了该正则表达式是否具有标志 i 。属性 multiline 是一个只读的布尔值,它说明了正则表达式是否具有标志 m 。最后一个属性是 lastIndex ,它是一个可读写的整数。对于具有标志 g 的模式,这个属性存储在字符串中下一次开始检索的位置。它由方法 exec() 和 test() 使用,我们在上一节中已经介绍过。

原创粉丝点击