JavaScript权威指南(个人笔记):(六)正则表达式
正则表达式(regular expression)是一个描述字符模式的对象。
JavaScript的RegExp类表示正则表达式。
String和RegExp都定义了方法,后者使用正则表达式进行强大的模式匹配,文本检索,替换功能
JavaScript中的正则表达式用RegExp对象表示。可以使用RegExp()构造函数来创建RegExp对象,不过RegExp对象更多的是通过一种特殊的直接量语法来创建。
例如:
let pattern = /s$/;
运行这段代码创建一个新的RegExp对象,并将他赋值给变量pattern。这个特殊的RegExp对象用来匹配所有以“s”结尾的字符串。
用构造函数RegExp()也可以定义一个与之等价的正则表达式,如下:
let pattern = new RegExp('s$');
程序运行时每次遇到对象直接量(初始化表达式)诸如 {} 和 [] 的时候都会创建新对象。比如,在循环体中写 let a = [],则每次遍历都会创建一个新的空数组。
正则表达式的模式规则是由一个字符序列组成的。
字符类
将直接量字符单独放进方括号内就组成了字符类(character class)。一个字符类可以匹配它所包含的任意字符。因此正则表达式 /[abc]/ 就和字符'a','b'和'c'中的任意一个都匹配
可以通过“^”符合来定义否定字符类,他匹配所有不包含在方括号内的字符。正则表达式 /[^abc]/ 匹配的是'a','b','c'之外的字符
重复
在使用 “*” 和 “?” 的时候要注意,因为可能匹配0个字符,所以他们允许什么都不匹配。
例如:正则表达式/a*/实际上与字符串'bbb'匹配,因为字符串中含有0个a
匹配重复字符是尽可能多的匹配,而且允许后续的正则表达式继续匹配。因此,我们称之为“贪婪”匹配。
我们同样可以使用正则表达式进行非贪婪匹配。需要在待匹配的字符后面跟随一个问号即可:“+?”
正则表达式/a+/可以匹配一个或多个连续的字母a。当使用“aaa”作为匹配字符串时,正则表达式会匹配他的三个字符。
但是/a+?/也可以匹配一个或多个连续字母a,但他是尽可能少的匹配,后一个模式只匹配第一个a。
使用非贪婪的匹配模式所得到的结果可能和期望并不一致。
正则表达式/a+b/,他可以匹配一个或多个a,以及一个b。当使用“aaab”作为匹配字符串时,他会匹配整个字符串。
非贪婪匹配的版本/a+?b/,他匹配尽可能少的a和一个b。你期望是匹配一个a和最后一个b。但实际上,这个模式却匹配了整个字符串,和该模式的贪婪匹配一模一样。
这是因为正则表达式的模式匹配总会寻找字符串中第一个可能匹配的位置。由于该匹配是从字符串中的第一个字符开始的,因此在这里不考虑他的子串中更短的匹配。
选择,分组和引用
注意:选择项的尝试匹配次序是从左到右,直到发现了匹配项。如果左边的选择项匹配,就忽略右边的匹配项,即使他产生更好的匹配。
因此,当正则表达式/a|ab/匹配字符串“ab”时,他只能匹配第一个字符
正则表达式中的圆括号有很多种作用。
例如,/java(script)?/可以匹配字符串“java”,后面可以有“script”,也可以没有。/(ab|cd)+|ef/可以匹配字符串“ef”,也可以匹配字符串“ab”或“cd”的一次或者多次重复。
在正则表达式中,圆括号的另一个作用是在完整的模式中定义子模式。当一个正则表达式成功和目标字符串相匹配时,可以从目标串中抽出和圆括号中的子模式相匹配的部分。
例如,假定我们正在检索的模式是一个或多个小写字母后面跟随了一位或多位数字,则可以使用模式/[a-z]+\d+/
但我们正则真正关心的是每个匹配尾部的数字,那么如果将模式的数字部分放在括号中(/[a-z]+(\d)+/) 就可以从检索到的匹配中抽取数字了。
修饰符
修饰符“i”用以说明模式匹配是不区分大小写的。
修饰符“g”说明模式匹配应该是全局,也就是说,应该找出被检索字符串中所有的匹配
用于模式匹配的String方法
str.search(pattern) 返回第一个与之匹配的子串的起始位置。如果匹配不到,将返回-1。参数是一个正则表达式
例子:
"JavaScript".search(/script/i); => 4
str.replace(pattern, targetStr) 用于执行检索和替换操作。第二个参数是要进行替换的字符串。
如果正则表达式中设置了修饰符g,那么源字符串中所有与之模式匹配的子串都将替换成第二个参数指定的字符串。
如果不带修饰符g,则只替换所匹配的第一个子串。
比如:将文本中的所有javascript(不区分大小写)统一替换为“JavaScript”:
str.replace(/javascript/gi, "JavaScript"); // 将所有不区分大小写的javascript都替换成大小写正确的JavaScript
但replace()的功能远不止这些。正则表达式中使用圆括号括起来的子表达式是带有从左到右的索引编号的,而且正则表达式会记忆与每个子表达式匹配的文本。
如果在替换字符串中出现了$加数字,那么replace()将用与指定的子表达式相匹配的文本来替换这两个字符。这时一个非常有用的特性。
比如,可以用它将一个字符串中的英文引号替换为中文半角引号:
let pattern = /"([^"]*)"/g; // 一段引用文本起始于引号,结束于引号。中间的内容区域不能包含引号。
str.replace(pattern, '“$1”'); // 用中文半角引号替换英文引号,同时要保持引号之间的内容(存储在$1中)没有被修改
str.match(pattern) 返回一个由匹配结果组成的数组。(参数是一个正则表达式,或通过RegExp()构造函数将其转换为正则表达式)如果该正则表达式设置了修饰符g,则该方法返回的数组包含字符串中的所有匹配结果。
例如:"1 plus 2 equals 3".match(/\d+/g) 返回["1", "2", "3"]
如果这个正则表达式没有设置修饰符g,match()就不会进行全局检索,它只检索第一个匹配。
但即使match()执行的不是全局检索,他也返回一个数组。在这种情况下,数组的第一个元素就是匹配的字符串,余下的元素则是正则表达式中用圆括号括起来的子表达式
因此,如果match()返回一个数组a,那么a[0]存放的是完整的匹配,a[1]存放的则是与第一个用圆括号括起来的表达式相匹配的子串。
以此类推,为了和方法replace()保持一致,a[0]存放的是$n的内容。
RegExp对象
正则表达式是通过RegExp对象来表示的。除了RegExp()构造函数之外,RegExp对象还支持三个方法和一些属性。
RegExp()构造函数带有两个字符串参数,其中第二个参数是可选的。RegExp()用以创建新的RegExp对象。
第一个参数包含正则表达式的主体部分,也就是正则表达式直接量中两条斜线之间的文本。
需要注意的是,不论是字符串直接量还是正则表达式,都使用“\”字符作为转义字符的前缀,因此当给RegExp()传入一个字符串表述的正则表达式时,必须将“\”替换成“\\”
RegExp()构造函数非常有用,特别是在需要动态创建正则表达式的时候,这种情况往往没办法通过写死在代码中的正则表达式直接量来实现。
例如,如果待检索的字符串是由用户输入的,就必须使用RegExp()构造函数,在程序运行时创建正则表达式。
RegExp的方法
RegExp.exec(str) 如果他没有找到任何匹配,他就返回null。
与String方法match()相似,但和match()方法不同的是,不管正则表达式是否具有全局修饰符g,exec()都会返回一样的数组。
相比之下,exec()总是返回一个匹配结果,并提供关于本次匹配的完整信息。
当调用exec()的正则表达式对象具有修饰符g时,他将把当前正则表达式对象的lastIndex属性设置为紧挨着匹配子串的字符位置。
当同一个正则表达式第二次调用exec()时,他将从lastIndex属性所指示的字符处开始检索。
如果exec()没有发现任何匹配结果,他会将lastIndex重置为0
比如:
let pattern = /Java/g;
let str = "JavaScript is more fun than Java";
console.log(pattern.exec(str)); // => index是0
console.log(pattern.exec(str)); // => index是28
RegExp.test(str) 对某个字符串进行检测,如果包含正则表达式的一个匹配结果,则返回true
当一个全局正则表达式调用test()时,他的行为和exec()相同。
这样一来,就可以使用test()来遍历字符串,就想用exec()方法一样。
同样要记住,正则表达式直接量的每次计算都会创建一个新的RegExp对象,每个新的RegExp对象具有各自的lastIndex属性
浙公网安备 33010602011771号