javascript正则表达式笔记
1. 记录
在ECMAScript 3中,
正则表达式字面量始终会共享同一个RegExp实例,而使用构造函数创建的每一个新RegExp实例都是一个新实例,如果在一个字符串中完成了一次模式匹配之后要开始检索新的字符串,就必须手动地把 lastIndex 属性重置为 0。
ECMAScript 5明确规定,使用正则表达式字面量必须像直接调用RegExp构造函数一样,每次都创
建新的RegExp实例。IE9+、Firefox 4+和Chrome都据此做出了修改
对于exec()方法而言,即使在模式中设置了全局标志(g),它每次也只会返回一个匹配项。在不
设置全局标志的情况下,在同一个字符串上多次调用exec()将始终返回第一个匹配项的信息。而在设
置全局标志的情况下,每次调用exec()则都会在字符串中继续查找新匹配项直至搜索到字符串末尾为止。在全局
匹配模式下,lastIndex的值在每次调用exec()后都会增加,而在非全局模式下则始终保持不变。
IE 的JavaScript 实现在lastIndex 属性上存在偏差,即使在非全局模式下,
lastIndex属性每次也会变化。
匹配双字节字符(包括汉字在内):[^\x00-\xff]
console.info( "abc汉字".replace( /[^\x00-\xff]/g,"aa").length ) // 7
匹配中文字符:[\u4e00-\u9fa5]
2. 正则替换 关于非匹配的一些问题
比如把一个字符串里面的img 标签找出来并为其外套上a标签
开始想到match匹配到img 标签 然后得到的结果再循环实现,后来看是否可以直接replace 实现
javascript的非匹配没有匹配多个字符作为一个整体的情况 比如 [^img] 只会匹配没有出现i并且没有m并且没有g的字符串
无法匹配img作为一个整体不出现的情况,后来只能用下面这种方式实现了 首先判断<符号 ,没有<符号的任意字符串0个或多个,再判断出现<符合但后面不是img字符的字符串0个或多个,这里用到0宽断言,记录下:
'daimgsdaf<img src="###" />d?s<a>asd</a>sfdsbasdfimggfg<fd><img src="###" />dddasdfasdf'.replace(/([^<]*(<(?!img))*)*(<img\s+[^/>]*\/>)([^<]*(<(?!img))*)*/g,'<a href="#">$3</a>');
不知道有没有更好的或其它的方式用一个replace实现。
3.知识点回顾
1.0宽断言
(?=test) 匹配后面是test的但是不捕获
(?!test) 匹配后面不是test的不捕获
(摘自正则30分钟):用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像\b,^,$那样用于指定一个位置。
例如千分符:'122231223123'.replace(/(?=(?!^)(\d{3})+$)/g,',');
2.贪婪与懒惰
*、+ 为贪婪 表示尽可能多的匹配
*?、+? 为懒惰 表示尽可能少的匹配
如:
"<div>a</div><div>b</div>".match(/<div>.*<\/div>/); 得到["<div>a</div><div>b</div>"]
.*表示除了回车以外0个或多个字符 尽可能多的匹配字符 直到遇到</div>结束 所以会匹配到最后的那个</div>
"<div>a</div><div>b</div>".match(/<div>.*?<\/div>/); 得到["<div>a</div>"]
.*? 表示除了回车以外0个或多个字符 尽可能少的匹配字符 直到遇到</div>结束 所以会匹配第一个出现的</div>
比如:'102400'.match(/^(\d+?)(0*)$/) ,其中(\d+?)分组表示尽可能少的匹配1个或多个数字(需要满足匹配整个表达式的情况下尽可能少的匹配),所以这里匹配的子分组分别是1024、00
3. match 与exec,replace
当match 没有全局匹配时 效果和exec 是一样的 返回匹配的数组 ,第一个元素为第一次出现的匹配的表达式,第二个元素为第一次匹配的表达式的子表达式如果有的话,第三个元素依此类推。
如:"abc abc".match(/a(bc)/); 返回["abc", "bc"] ;“abc abc”中第二个abc并不会匹配
当match 有全局匹配时 返回匹配的数组 ,数组每个元素对应匹配的表达式 ,这个时候子表达式将不会捕获
如:"abc abc".match(/a(bc)/g) 返回 ["abc", "abc"] ;全局匹配对应的表达式 ,子表达式没有捕获
replace 和 match 的全局匹配有点区别 ,replace 不管全局或非全局匹配都会捕获子表达式
如: "abc abc".replace(/a(bc)/,"$1"); 得到 "bc abc"
"abc abc".replace(/a(bc)/g,"$1"); 得到 "bc bc"
"aaabac".replace(/(\w)\1+/g,'-') 得到 "-bac"
另外正则对象在匹配后会保存上次匹配值的索引,因此再次使用同一个正则对象时最好先把正则对象的lastIndex值置为0
eg:var a = new RegExp("b","g");
console.log(a.test('abc')); //true
console.log(a.lastIndex); //2
console.log(a.test('abc')); //false
console.log(a.lastIndex) //0
4. 知识点总结
1.exec
语法:
reg.exec(str)
如
果 exec() 找到了匹配的文本,则返回一个结果数组。否则,返回 null。此数组的第 0 个元素是与正则表达式相匹配的文本,第 1 个元素是
与 RegExpObject 的第 1 个子表达式相匹配的文本(如果有的话),第 2 个元素是与 RegExpObject 的第 2 个子表达式
相匹配的文本(如果有的话),以此类推.
除了数组元素和 length 属性之外,exec() 方法还返回两个属性。index 属性声明的是匹配文本的第一个字符的位置。input 属性则存放的是被检索的字符串 string。
在调用非全局的 RegExp 对象的 exec() 方法时,返回的数组与调用方法 String.match() 返回的数组是相同的。
当 RegExpObject 是
一个全局正则表达式时。它会在 RegExpObject 的 lastIndex 属性指定的字符处开始检索字符串 string。
当 exec() 找到了与表达式相匹配的文本时,在匹配后,它将把 RegExpObject 的 lastIndex 属性设置为匹配文本的最后一个
字符的下一个位置。这就是说,您可以通过反复调用 exec() 方法来遍历字符串中的所有匹配文本。当 exec() 再也找不到匹配的文本时,它将返
回 null,并把 lastIndex 属性重置为 0。
eg:
/123/.exec('123 123'); //返回["123"]
全局reg时 如果要匹配所有 要通过循环匹配实现:
var a; var r=[]; var str="123 123 ab1232"; var reg = new RegExp("123","g"); while((a=reg.exec(str))!=null){ console.log(a); console.log(reg.lastIndex); // r.push(a[0]); } console.log(r) //所有匹配的
2.match
语法:
stringObject.match(regexp)
如果 regexp 没有标志 g,那么 match() 方法就只能在 stringObject 中执行一次匹配。如果没有找到任何匹配的文本, match() 将返回 null。否则,它将返回一个数组。
如
果 regexp 具有标志 g,则 match() 方法将执行全局检索,找到 stringObject 中的所有匹配子字符串。若没有找到任何匹配
的子串,则返回 null。如果找到了一个或多个匹配子串,则返回一个数组。不过全局匹配返回的数组的内容与前者大不相同,它的数组元素中存放的
是 stringObject 中所有的匹配子串,而且也没有 index 属性或 input 属性。
3.零宽断言
所谓0宽断言 是指捕获的宽度为0 ,也就是只匹配不捕获,括号不会引起分组。
(?=X)正向断言,假定该位置后跟的是X
(?!X) 正向否定断言,假设该位置后跟的不是X
(?<=X)反向断言,假设该位置前跟的是X
(?<!X)反向否定断言,假设该位置前跟的不是X
可惜javascript不支持反向断言 (?<=X) 和 (?<!X)
还有个(?:X) 在js中这个应该不能称为0宽断言,因为它会捕获。
(?:X)是该位置出现X,并且捕获X,但是这里的括号不会引起分组。
比如:
/123(a)/.exec('123 123a'); //返回 ["123a","a"] //匹配123后面是a的字符串 ,常规的括号引起了分组
/123(?:a)/.exec('123 123a'); //返回["123a"] 匹配123后面是a的字符串并且捕获了a 但是不会分组
/123(?!a)/.exec('123a 123'); //返回 ["123"] 匹配该位置后面不是a的字符串,也就是在正则‘123’的后面不是a的字符串。 没有捕获a也没有引起分组
/123(?=a)/.exec('123a 123'); //返回 ["123"] 匹配该位置后面是a的字符串,也就是在正则‘123’的后面是a的字符串。 没有捕获a也没有引起分组
匹配html的应用:
/<html>(?=([\s\S]*?<head>))\1(?=([\s\S]*?<title>))\2(?=([\s\S]*?<\/title>))\3(?=([\s\S]*?<\/head>))\4(?=([\s\S]*?<body>))\5(?=([\s\S]*?<\/body>))\6[\s\S]*?<\/html>/
五. 附加知识点:
使用RegExp的显式构造函数,语法为:new RegExp("pattern"[,"flags"])。
使用RegExp的隐式构造函数,采用纯文本格式:/pattern/[flags]。
pattern部分为要使用的正则表达式模式文本,是必须的。
在第一种方式中,pattern部分以JavaScript字符串的形式存在,需要使用双引号或单引号括起来,这时首尾不需要'/'。
在第二种方式中,pattern部分嵌套在两个“/”之间,不能使用引号。
由于JavaScript字符串中的“\”是一个转义字符,因此,使用显式构造函数创建RegExp实例对象时,应将原始正则表达式中的“\”用“\\”替换
如:var reg1 = new RegExp("\\d{2}");
var reg2 = /\d{2}/;
var reg = new RegExp("^(\\+|-)?\\d+(\.\\d{0,2})?$"); //可有两位小数的浮点数
含变量的时候:
var a = "abc",
reg3 = new RegExp("^\\d" + a + "\\d$");
由于正则表达式模式文本中的转义字符也是“\”,如果正则表达式中要匹配原义字符“\”,在正则表达式模式文本中要以“\\”来表示,当使用显式构造函数的方式创建RegExp实例对象的时候,就需要使用“\\\\”来表示原义字符“\”。
var reg = new RegExp(\\\\)。
下面再复习下最常用的 test exec match search replace indexOf split 7个方法
var str = "" ,str1 = ""; //字符串
var reg = new RegExp(); //reg对象
var x = [] ; //返回的数组
g //全局匹配
i //不区分大小写
m //多行模式匹配
1.test
格式: reg.test(str); // 返回true 或false;
eg: /(a)\w+/.test("abcde") //返回true;
2.exec
格式:reg.exec(str); //返回一个数组 数组第一项为匹配的表达式 如果有匹配的子表达式则数组第二项为子表达式 以此类推。exec永远返回与第一个匹配相关的信息,不管有没有g
eg: /a(b)\w+/.exec("abcdef"); //返回的数组 x[0] = “abcdef” ,x[1] = "b";
/ab\w+/g.exec("abcde.fabc") //返回"abcde"
3.match
格式:str.match(reg); //返回一个数组 如果没有全局标识符g 则和exec一样,第一项 为匹配的表达式, 如果有匹配的子表达式则数组第二项为子表达式 以此类推。如果reg有全局标识符g 则 第一项 为第一个匹配的表达式,第二项 为第二个匹配的表达式 以此类推。
eg : "abcdef.abc".match(/a(b)\w+/); 返回 x[0] = “abcdef”,x[1] = "b";
"abcdef.abc".match(/a(b)\w+/g); 返回 x[0] = “abcdef” ,x[1] = "abc";
有关exec 和 match 的区别 这里有篇文章说的比较详细:http://makemyownlife.iteye.com/blog/395172
4.search
格式: str.search(reg); 返回从字符串中寻找指定值的位置,如果没找到则返回-1。它将忽略标志g。它同时忽略regexp 的lastIndex 属性,并且总是从字符串的开始进行检索。
eg: "abcdef".search(/a/) //返回0
5. indexOf
格式:str.indexOf(str1[,startIndex]) ; 返回从字符串中寻找指定值的位置,如果没找到则返回-1,可以指定开始检索的位置 ,和search的区别是str1不能是正则表达式。
6.replace
格式:str.replace(reg,str1) ; 返回 替换后的字符串 支持 i、g、m
eg: "abcdef".replace(/a/,"b") 返回:“bbcdef”;
"abcdefaa".replace(/a/ig,"b") 返回:“bbcdefbb”;
当str1为function时 返回的$1为正则表达式相匹配的文本,$2为子表达式匹配的文本,依此类推。
当str1为$1$2...时,则$1$2分别匹配相对应的子表达式。
eg:
var a = "abcealsk";
var c1 = a.replace(/(ab)c/ig,function($1,$2){return $1 + $2}); //返回:abcabealsk
var c2 = a.replace(/(ab)c/ig,"$1$2"); //返回:ab$2ealsk //此时$2没有对应的字表达式被当做字符串返回了。
比如常用的去掉字符串两端的空格
var a = " a a ";
var b = a.replace(/(^\s*)|(\s*$)/g,''); 或 var b = a.replace(/^\s*(\w+.*\w+)*\s*$/,'$1');
b为‘a a’
7.split
格式:str.split(reg); 返回按照reg划分后的数组 支持i
eg: "a_b_B_d_e".split(/b/i); 返回:[a_,_,_d_e]
练习:
测试是否含重复字符:
var a = "abcdefga";
var c = "abcdefg";
var b = /(\w)\w*\1/;
console.log(b.test(a)) //true
console.log(b.test(c)) //false
\m 表示第m次匹配的内容
eg:
/(span)\1/.test('spanspan') //true
/(span)(div)(td)\1\2\3/.test('spandivtdspandivtd'); //true
eg 匹配xhtml标签 /^<(\w+)\s*.*>.*<\/\1>|<\w+\s*\/>$/.test("<span><img/></span>")
function escapeRegExp(string){
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string 整个匹配的字符串
}
function format (num) {
return (num.toFixed(2) + '').replace(/\d{1,3}(?=(\d{3})+(\.\d*)?$)/g, '$&,'); //千分位逗号表示 $&表示整个匹配的字符串
}
或:'|' 匹配子表达式两边的内容,
比如:
/^(jpg)|(png)$/.test('png') 匹配^(jpg)或(png)$
/^(jpg|png)$/.test('png') 匹配^(jpg)$或^(png)$
参考文档:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions