正则表达式对象:
RegExp对象表示正则表达式,他是对字符串执行模式匹配的强大工具。
创建正则表达式的方式:
- 通过字面量的方式创建(隐式创建Reg对象) var re = /pattern/[flag]
- 说明:pattern是必须的,是我们要匹配的内容,也就是创建的规则,而flag是可有可无,根据具体需求添加。
- flag--》有几种选择:
- i 忽略大小写->ignore case 默认是大小写敏感
- g 全局匹配->global 默认是搜索到第一个匹配就停止
- m 多行匹配-> multiple line 默认是单行匹配
- var re = /a/g; --全局匹配,以字母a开头,以字母a结尾
- 通过构造函数创建正则表达式(显示创建RexExp对象) var re = new RegExp("pattern","[flag]");
- 如 var re = new RegExp("d","g"); 全局匹配数字
- 如:var re = new RexExp("a","g"); 全局匹配,以字母a开头,以字母a结尾
RegExp对象方法:
-
- exec()方法
- test()方法
-----------------------------------------------分隔符-------------------------------------------------
String 对象与正则表达式有关的方法:
这四个方法是属于String对象的,不是属于RegExp对象的
str.search(s)用于检测子字符串,如果s存在与str串中,就是匹配到了,返回第一个匹配的结果,如果有多个s,也只返回第一个被匹配到的index(index从0开始计数)
search()方法,如果没有匹配到,就返回-1
search()方法不执行全局匹配,它将忽略标志g,并且总是从字符串的开始位置进行检索
var s = "1234567891";
console.log(s.search("1")); //0 index是0
console.log(s.search(/1/)); // 0
console.log(s.search(/1/g)); //0 忽略g
match()方法--》将检索字符串,以找到一个或多个与regExp匹配的文本 ; regExp是否具有g标志,对结果是有影响的。
非全局调用,它的lastIndex也不起作用,为0.
全局调用的情况下,match的方法中没有index属性,如果你去打印,会返回undefined,而lastIndex依旧为0.
'a,b,c,d'.split(,); 写成这样,它会尝试将它转换为'a,b,c,d'.split(/,/);
split还可以传入更为复杂的正则进行字符串的切割。
这个参数的位置是固定的,第一个参数是固定的;第二个参数如果有分组,那就是分组的内容,有几个分组,就是几个参数;如果第二个没有分组,第三个参数就是第二个形参,index是每次匹配结果的位置;第四个是每次匹配的字符串的内容,因为在回调函数中,每次回调,都会改变第四个参数的值。
用function返回的值作为替换的结果
"a1b2c3d2".replace("2","哈"); //"a1b哈c3d2"
"a1b2c3d2".replace(2,"哈"); //"a1b哈c3d2"
//上面直接写2,它会隐式的换成/2/
"a1b2c3d2".replace(/2/,"哈"); //"a1b哈c3d2"
"a1b2c3d2".replace(/d/,"哈"); //"a哈b2c3d2"
//一次性匹配多个数字,并且替换
"a1b2c3d2".replace(/d/g,"哈"); //"a哈b哈c哈d哈"
"a1c2ded3r4a3".replace(/d/g,function(match,index,origin) {
console.log("匹配字符串:"+match+" "+"index:"+index + " "+"原字符串:"+origin);
return parseInt(match)+1;
});
用function返回的值作为替换的结果
------------------------------------------------分隔符-------------------------------------------------
正则表达式由2种基本组成
原义文本字符 例如: a c d se e等
元字符
元字符是在正则表达式中有特殊含义的非字母字符
例如: * + ? ~ ^ . | ( ) { } [ ] 这些都是有含义的
[abc] --> 表示匹配的字符是a或者 b 或者c
[^abc] --> 表示匹配的字符不包含a 或者b或者c
匹配小写字母a-z [a-z]
大写字母A-Z [A-Z]
匹配0-9,a-z,A-Z [0-9a-zA-Z]
日期: 2017-5-31 如果想匹配中间的横线 /[0-9-]/
var date = "2017-5-21".replace(/[0-9-]/g,"A"); ---> AAAAAAAAA
将中间的横线替换掉 var date = "2017-5-21".replace(/-/g,"/"); ---> 2017/5/21
var date = "2016-3-3".replace(/0-9/g,"A"); -->AAAA-AA-AA
--------------------------分割线-------------------------------------------------------------------
水平制表符 | |
v | 垂直制表符 |
回车符 | |
换行符 | |
o | 空字符 |
f | 换页符 |
cX | 与X对应的控制字符(ctrl+x) |
等价于 | ||
. | 除了回车符和换行符的所有字符 | [^ ] |
d | 数字字符 | [0-9] |
D | 非数字字符 | [^0-9] |
s | 空白符 | [ vf ] |
S | 非空白符 | [^ vf ] |
w | 单词字符,数字,下划线 | [a-zA-Z0-9_] |
W | 非单词字符 | [^a-zA-Z0-9_] |
测试上面的.这个 这是不能匹配到回车和换行符
现在来试试空白符 空白符被替换了
测试s 空白符 蓝色部分有一个空格,里面和s等价的都被替换了,空格也被替换了
测试w -->单词字符,数字,下划线 正常的匹配到了,除了空格,逗号,点等一些符号,应该还有其他的不会被匹配到比如分好,冒号等
现在是W --》非单词字符 《==》 [^0-9a-zA-Z] 汉字也被匹配到了
下面的示例是如何只匹配单个汉字
汉字的字符在 [u4e00-u9fa5]之间
如果汉字被包裹在一些字符中间,我们如果连那些字符也要一起匹配的话,就需要像下面那样进行转义
^ | 以什么开头 |
$ | 以什么结尾 |
单词边界 | |
B | 非单词边界 |
? | 次数出现0次或者1次(最多出现1次) |
+ | 出现1次或多次(最少出现1次) |
* | 出现0次或多次(任意次数) |
{n} | 出现n次 |
{n,m} | 出现n到m次 {1,5} 出现1到5次,包含1和5 |
{n,} | 至少出现n次以上 {2,} 至少2次以上 |
?=n | 匹配任何其后紧接指定字符串 n 的字符串。 |
!?n | 匹配任何其后没有紧接指定字符串 n 的字符串。 |
正则表达式的前瞻:
正则表达是从文本头部向尾部开始匹配,文本尾部的方向称为前。前瞻就是正则表达式匹配到规则的时候,向前检查是否符合断言 (断言就是前瞻语法的一部分)
符合和不符合特定的断言称为肯定/正向匹配和否定/负向匹配
前瞻和后顾方向相反,javascript不支持后顾,所以不作说明。
名称 | 正则表达式 | 含义 | |
正向前瞻 | exp(?=n) | 量词匹配任何其后紧接指定字符串 n 的字符串。 | javascript支持 |
负向前瞻 | exp(!?n) | 量词匹配其后没有紧接指定字符串 n 的任何字符串。 | |
正向后顾 | exp(?<=assert) | javascript不支持 | |
负向后顾 | exp(?<!=assert) |
var s = "asdf2e2ed";
//括号里面是断言部分,不会替换
console.log(s.replace(/w(?=d)/g,"换"));
//结果: "asd换2换2ed"
//对其后紧跟 "all" 的 "is" 进行全局搜索:
var str="Is this all there is";
var patt1=/is(?= all)/g
;
//所以is被匹配到
"a2c3deda#v@".replace(/d(?=w)/g,"是"); //前瞻
// 结果 "a是c是deda#v@"
第一个是前瞻,就是要求王武的前面必须是张二,符合条件,将张二替换掉,而王武只是断言,不会被替换
第二个是后瞻,要求张二的后面不能是王武,符合条件,将张二替换掉。
分组
用()可以到达分组的目的,例如:可以使量词作用于分组
如果不分组,量词只作用于离它最近的内容
情景假设:要匹配某个一单词连续出现3次,该如何写正则表达式?
上面这个做法是不对的,原因是量词3次是只作用在d上面,所以返回false
下面就是运用分组,将整个目标包裹起来,让量词作用于分组
情景模拟:第一个是字母,紧跟这是2个数字的一个字符串
正则表达式中的 或 |
下面的是用[]来表示
分组中的反向引用
有分组才有反向引用,它的表示方式是$ ,单纯的$在没有分组的情况下,是表示以什么结尾
//年-月-日
var s2 = "2017-05-12";
//目标:月-日-年
console.log(s2.replace(/(d{4})-(d{2})-(d{2})/g,"$2-$3-$1"));
//结果:"05-12-2017"
让日期 不区分-和/
//年-月-日
var s2 = "2017-05-12";
var s3 = "2017/05/12";
//目标:月-日-年
console.log(s2.replace(/(d{4})[-/](d{2})[-/](d{2})/g,"$2-$3-$1"));
//"05-12-2017"
console.log(s2.replace(/(d{4})[-/](d{2})[-/](d{2})/g,"$2-$3-$1"));
//"05-12-2017"
忽略分组:当不希望捕获某个分组时,只需要在分组( 后面加?:即可
此时,不能再写$3,因为$3不存在,$2对应的是12,$1对应的是2017
--------------------------------分割线------------------------------------
正则表达式的贪婪模式(针对量词而言的模式):
它会尽可能多的去匹配,所以会以3位为基准,进行替换
正则表达式的非贪婪模式(针对量词而言的模式):
它会尽可能少的去匹配,也就是说,一旦匹配成功,它不会再继续尝试匹配
做法:在量词后面加?
这里,就是以2位为基准,进行替换,它不会再尝试以3位为基准的这个情况
RegExp 对象属性
RegExp 对象方法
注意:
var reg = /w/; console.log(reg.test("s")); //true console.log(reg.test("s")); //true console.log(reg.test("s")); //true
var reg = /w/g;
console.log(reg.test("a")); //true
console.log(reg.test("a")); //false
console.log(reg.test("a")); //true
出现上面那种情况的原因:是lastIndex在作怪,lastIndex受到了影响
lastIndex:当前匹配结果的 最后一个字符 的 下一个字符的位置
var reg = /w/;
while(reg.test("ab")) {
console.log(reg.lastIndex);
}
//1
//2
上面while先匹配a,即a就是匹配的结果,而a就一个字符,它也会死最后一个字符,a的下一个字符就是b,所以lastIndex先返回位置1。
此时,将上次匹配的结果(位置)记住了,然后往下继续找
同理:b是当前匹配的结果,b也是最后一个字符,b的下一个位置就是2,所以lastIndex返回位置2
到第3次循环的时候,后面没有内容了,lastIndex又被重置为0,而0在js中代表false,所以第三次循环就直接退出去了。
对于reg.test("ab");一会儿出现true,一会出现false的情况,有一种不是特别好的办法来解决:
(/w/g).test("ab");
这种方法每次的结果都为true,但是这种方法的原理是:每次都重新实例化一个正则对象,缺点:消耗内存,每次实例化都需要内存开销,而且没必要这样做,因为test()方法的本意就是测试有没有这么一个目标字符串。
用var reg = /w/; reg.test("ab");不用加global 它一检测到a,就返回true,就不再往下匹配。
问题:如果想知道,我们在一串字符串中的哪个位置匹配的,就用exec()方法
exec() 方法的功能非常强大,它是一个通用的方法,而且使用起来也比 test() 方法以及支持正则表达式的 String 对象的方法更为复杂。
- 使用正则表达式对字符串进行搜索,并会更新RegExp对象的属性(lastIndex) 以反映匹配的结果
- 如果没有匹配到文本,就返回null,否则就返回一个数组
数组的两个额外属性
-
- index 声明匹配文本第一个字符的位置 (计数是从0开始的)
- input 存放被检索的字符串string
匹配过程:
如果 exec() 找到了匹配的文本,则返回一个结果数组。否则,返回 null。
此数组的第 0 个元素是与正则表达式相匹配的文本
第 1 个元素是与 RegExpObject 的第 1 个子表达式相匹配的文本(如果有的话)
第 2 个元素是与 RegExpObject 的第 2 个子表达式相匹配的文本(如果有的话)
以此类推。。。。
我们可以看得出,在调用非全局的 RegExp 对象的 exec() 方法时,返回的数组与调用方法 String.match() 返回的数组是相同的。
在非全局下,exec()方法的lastIndex属性根本不会生效,始终返回0.
但是,当 RegExpObject 是一个全局正则表达式时,exec() 的行为就稍微复杂一些。它会在 RegExpObject 的 lastIndex 属性指定的字符处开始检索字符串 string。
当 exec() 找到了与表达式相匹配的文本时,在匹配后,它将把 RegExpObject 的 lastIndex 属性设置为匹配文本的最后一个字符的下一个位置。这就是说,您可以通过反复调用 exec() 方法来遍历字符串中的所有匹配文本。当 exec() 再也找不到匹配的文本时,它将返回 null,并把 lastIndex 属性重置为 0。
var reg = /d(w)d/;
var reg2 = /d(w)(w)d/g;
var s = "$ed1se2*d3rw3e4";
var ret = reg.exec(s);
//console.log("lastIndex->"+reg.lastIndex+" "+ret.toString()+" index->"+ret.index);
//console.log("lastIndex->"+reg.lastIndex+" "+ret.toString()+" index->"+ret.index);
while(ret=reg2.exec(s)) {
console.log("lastIndex->"+reg2.lastIndex+" "+ret.toString()+" index->"+ret.index);
}
前面2次使用reg不再全局匹配,lastIndex都为0,表示不生效。那个e是3e4的子串,如果没有分组,就没有这个子串。
提示和注释
重要事项:如果在一个字符串中完成了一次模式匹配之后要开始检索新的字符串,就必须手动地把 lastIndex 属性重置为 0。
提示:请注意,无论 RegExpObject 是否是全局模式,exec() 都会把完整的细节添加到它返回的数组中。这就是 exec() 与 String.match() 的不同之处,后者在全局模式下返回的信息要少得多。因此我们可以这么说,在循环中反复地调用 exec() 方法是唯一一种获得全局模式的完整模式匹配信息的方法。
-----------------------------------------分割线--------------------------------------------------------------------------------------------
实例:
匹配ab+数字+任意字符的字符串 | reg=/abd./ | reg=/ab[0-9][^ ]/ | reg=/ab[0-9]./ |
2015-12-31 | reg=/d{4}-d{2}-d{2}/ | ||
将上面的日期换成12-31-2015 | "2015-12-31".replace(/(d{4})[-/](d{2})[-/](d{2})/,"$2-$3-$1")); | ||
^表示边界 str = "aabbe" | /^a/.test(str); 返回true | ||
$表示结尾 str = "aabbcc" | /c$/.test(str); 返回treu | ||
单词边界 var str = "asdasd a"; | /a/g.test(str); 返回true | ||
B非单词边界 | |||
^a$ 以a开头,a结尾 与/a/g有点不同 | 如果str = "abcde a" 那么/^a$/g 和/a/g就不同 | 如果 str="a" 那么二者结果就相同 | |
var s = "123456789".replace(/d{2,3}/,"a"); | 结果"aaa" --这里涉及到正则表达式的贪婪模式 尽可能多的匹配,所以会以3位为基准,进行替换 | ||
"335a22be".replace(/d{2,3}?/g,"换"); | "换5a换be" --这里涉及到正则表达式量词里面的非贪婪模式 | ||