【犀牛书笔记】JavaScript正则表达式的模式匹配
正则表达式,是一个描述字符模式的对象。
JavaScript用RegExp类表示正则表达式
String和RegExp都定义了相关方法
原创文章,转载请注明:http://www.cnblogs.com/phpgcs/
通过正则表达式进行强大的“模式匹配”和“文本检索与替换”功能
- 内容1:正则表达式的定义
- 直接量字符
- 字符类
- 重复
- 选择,分组,引用
- 指定匹配位置
- 修饰符
- 内容2:用于模式匹配的String方法
- search()
- replace()
- match()
- split()
- 内容3:regexp对象
- exec()
- test()
构建正则对象的方法:
- var pattern = /s$/;
- var pattern = new RegExp("s$");
10.1 正则表达式的定义
10.1.1 直接量字符
所有的字母/数字 都是按照字面含义进行匹配
非字母的字符匹配需要通过 \ 前缀进行转义
- \o NULL(\u0000)
- \t 制表符(\u0009)
- \n 换行符(\u000A)
- \v 垂直制表符(\u000B)
- \f 换页符(\u000C)
- \r 回车符(\u000D)
- \xnn 由16进制nn指定的拉丁字符
- \uxxxx 由16进制xxxx指定的Unicode字符
- \cX 控制字符^X
特殊含义的标点符号:
^ $ . + ? ! : | \ / ( ) ] \ { }
某些符号只有在正则的某些上下文中才有某种特殊含义,在其他地方则当成直接量处理
通行的规则:用前缀\来匹配特殊字符的直接量
对于数字和字母,尽量不要用 \ 前缀,那是有特殊意义的。
10.1.2字符类
将直接量字符 单独放进 [] 就组成了字符类(character class)
/[abc]/匹配 a b c的任意一个
/[^abc]/定义来一个“否定字符类”,匹配除了a b c的字符
常用:/[a-z]/,/[a-zA-Z0-9]/
- [...]
- [^...]
- . 任意字符,除了换行符/其他Unicode行终止符号
- \w 任何ASCII组成的单词,等价[a-zA-Z0-9]
- \W 任何非。。。。,等价[^a-zA-Z0-9]
- \s 任意Unicode空白符
- \S 任意非Unicode空白符,区分跟\w的区别
- \d 任意ASCII数字,等价[0-9]
- \D [^0-9]
- [\b] 退格直接量
10.1.3重复
- ?
-
-
这些重复默认都是“贪婪的”,允许后续的正则表达式继续匹配。
举例:
/a+/匹配"aaa"结果"aaa"
/a+b/匹配"aaab"结果“aaab"
非贪婪模式,在重复字符后面加上?
举例:
/a+?/匹配"aaa"结果"a",第一个"a"
/a+?b/匹配"aaab"结果“aaab",跟贪婪一样
它是应该匹配尽可能少的"a"和一个"b",而匹配总是会寻找字符串中”第一个可能匹配的位置“
思考:如果就要匹配"ab"呢?
10.1.4 选择、分组和引用
选择有个特点,就是一旦匹配到左边的结果,就会忽略右边的
/a|ab/匹配"ab"结果"a"
这里要说说圆括号()的多个作用:
- 把单独的项组合成子表达式,(...)以便可以用 | * + ? 来对这个子表达式进行处理
/java(script)?/可以匹配"java"也可以匹配"javascript" - 在完整的模式中定义子模式,以便后续的处理
- 允许在同一个正则表达式的后部引用前面的子表达式
通过\1,\2...实现
常用的例子比如匹配html标记 <(\S?)[^>]>.?</\1>|<.? />
再比如/(['"])[^'"]*\1/匹配"display:none;"这样子左右侧的引号要相匹配
正则不允许用"括起的内容中有',反之亦然
不允许在“字符类”中使用\1这种引用
(?:[Ss]cript)仅仅用于分组,而不生成引用
- | 选择
- (...) 组合
- (?:...) 只组合,但不记忆
- \n 引用记忆
10.1.5指定匹配位置(锚)
- \b 匹配单词边界,即位于\w \W之间的边界,注意[\b]匹配的则是退格符
- ^
- $
- \B
- (?=p) 零宽正向先行断言,要求接下来的字符都与p匹配,但不包括匹配p的那些字符
- (?!p) 零宽负向先行断言
/^JavaScript$/匹配"JavaScript"
/\s\Java\s/匹配" Java ",无法匹配到开头/结尾的"Java",也有多余的空格
/\bJava\b/就可以补充以上2点
/\Bscript/匹配"postscript",而不匹配"script","scripting"
任意正则表达式都可以作为一个锚点条件
先行断言:(?=...)
/[Jj]ava([Ss]cript)?(?=:)/ 只在后面有冒号时才匹配
匹配"JavaScript: The Definitive Guide"中的Javascript
不匹配"Javascript in a Nutshell"中的Javascript
负向先行断言:(?!...), 用以指定接下来的字符不必匹配
/Java(?!Script)([A-Z]\w*)/
匹配"Java"后跟随一个大写字母和任意多个ASCII单词,但是后面不能跟随Script
匹配成功:"JavaBeans"
匹配失败:"Javanese","JavaScript","JavaScripter"
10.1.6修饰符
- i 不区分大小写
- g 全局匹配,找到所有的匹配
- m 多行匹配模式,^匹配一行的开头和字符串的开头
- $匹配行的结束和字符串的结束
10.2 用于模式匹配的String方法
- search()
- replace()
"JavaScript".search(/script/i);
4
"JavaScript".search(/script/);
-1
不支持全局搜索,会忽略/g
"abababab".replace(/a/gi,"A");
"AbAbAbAb"
"abababab".replace(/a/gi,"M");
"MbMbMbMb"
"1 plus 2 equals 3".match(/\d+/g);
["1", "2", "3"]
"1 plus 2 equals 3".match(/\d+/);
["1"]
var text="http://www.cnblogs.com/phpgcs";
var url=/(\w+):\/\/([\w.]+)\/(\S*)/;
text.match(url);
["http://www.cnblogs.com/phpgcs", "http", "www.cnblogs.com", "phpgcs"]
//给match方法传入一个非全局的正则表达式,实际上跟exec()方法是一样的
"123,456,789".split(",")
["123", "456", "789"]
"123 , 456, 789".split(/\s*,\s*/);
["123", "456", "789"]
10.3 RegExp对象
var zipcode = new RegExp("\d{5}", "g");
这个构建函数非常有用,尤其是需要动态创建正则表达式的时候,比如用户输入。
(通过eval()也可以实现在运行时动态创建这个正则,但是不推荐)
另外,注意这个例子中的\,而不是,无论是“字符串直接量”还是“正则表达式”,都要使用""字符作为”转义字符前缀“
除了构建正则之外,RE对象还支持3个方法和一些属性
5个属性:
var zipcode=new RegExp("\\d{5}", "g");
undefined
zipcode
/\d{5}/g
zipcode.source
"\d{5}"
zipcode.global
true
zipcode.ignoreCase
false
zipcode.multiline
false
zipcode.lastIndex
0
2个方法:
exec(),test()
var pattern = /Java/g;
var text = "JavaScript is more fun than Java!";
var result;
while((result = pattern.exec(text))!=null){ console.log("Matched '"+result[0]+"'"+" at position "+result.index+";next search begins at "+pattern.lastIndex);}
Matched 'Java' at position 0;next search begins at 4
Matched 'Java' at position 28;next search begins at 32
var pattern = /Java/g;
"JavaScript".test(pattern);
pattern.test("JavaScript");
true
对比下这4+2=6种方法:
String:search(),replace(),match(),split()
RegExp:exec(),test()
和 match() 不同,无论是否有 g,exec()都会返回一样的数组
当 exec() 含 g,情况较复杂
将会把当前pattern.lastIndex设置为紧邻匹配子串的字符位置(如上例子)
当同一个pattern再一次调用exec(),将从lastIndex开始检索
如果没有发现匹配结果,pattern.lastIndex重置0
var pattern = /Java/g;
var text = "JavaScript is more fun than Java Java Java!";
text.match(pattern);
["Java", "Java", "Java", "Java"]
pattern.exec(text);
["Java"]
pattern.lastIndex
4
pattern.exec(text);
["Java"]
pattern.lastIndex
32
pattern.exec(text);
["Java"]
pattern.lastIndex
37
pattern.exec(text);
["Java"]
pattern.lastIndex
42
pattern.exec(text);
null
pattern.lastIndex
0
pattern.exec(text);
["Java"]
pattern.lastIndex
4
如果让一个带g的pattern对“多个”字符串执行exec()或者test(),
要么需要在每个string中找出所有的匹配,以便将lastIndex自动重置0
要么需要显式将lastIndex手动设置0(最后一次检索失败时)
当然,如果RegExp不带有g修饰,则不必担心这个问题。
在ECMAScript5中,正则的每次计算都会创建一个新的RegExp对象,各自有各自的lastIndex属性,将大大减少“残留”lastIndex对程序造成的意外影响。
String的方法search(),replace(),match()并不会用到 lastIndex属性,只是简单的设置为0