一直都是在用正则表达式,没有好好的汇总过。正好,遇到很多小算法,可以作为案例补充。
正则表达式的定义:
正则表达式是由普通字符和特殊字符(也叫元字符或限定符)组成的文字模板。
元字符:
. 匹配除换行符(\n\r)以外的任意字符
\d 匹配数字,等价于字符组[0-9]
\w 匹配字母,数字,下划线或汉字
\s 匹配任意的空白符(包括制表符,空格,换行等)
\b 匹配单词开始或结束的位置
^ 匹配行首
$ 匹配行尾
反义元字符
\D 匹配非数字的任意字符,等价于[^0-9]
\W 匹配除字母,下划线或汉字之外的任意字符
\S 匹配非空白的任意字符
\B 匹配非单词开始或结束的位置
[^x]匹配除x以外的任意字符
重复限定符:
限定符共有6个,假设重复次数为x次,那么将有如下规则:
* x>=0
+ x>=1
? x=0 or x=1
{n} x=n
{n,} x>=n
{n,m} n<=x<=m
字符组
[…] 匹配中括号内字符之一。 如: [xyz] 匹配字符 x,y 或 z. 如果中括号中包含元字符,则元字符降级为普通字符,不再具有元字符的功能,如 [+.?] 匹配 加号,点号或问号.
排除性字符组
[^…] 匹配任何未列出的字符,. 如: [^x] 匹配除x以外的任意字符.
多选结构
| 就是或的意思,表示两者中的一个. 如: a|b 匹配a或者b字符.
括号
括号 常用来界定重复限定符的范围,以及将字符分组. 如: (ab)+ 可以匹配abab..等,其中 ab 便是一个分组.括号起到字符分组或者捕获作用。
转义字符
通常 * + ? | { [ ( ) ] }^ $ . # 和 空白 这些字符都需要转义.
操作符的运算优先级
1、转义符
2、(),(?:),(?=),[] 圆括号或方括号
3、*,+,?,{n},{n,},m} 限定符
4、^,$ 位置
5、| "或" 操作
修饰符
javaScript中正则表达式默认有如下五种修饰符:
g (全文查找) i (忽略大小写查找) m (多行查找) y (ES6新增的粘连修饰符) u (ES6新增)
常用的正则表达式
汉字: ^[u4e00-u9fa5]{0,}$ Email: ^w+([-+.]w+)*@w+([-.]w+)*\.w+([-.]w+)*$ URL: ^https?://([w-]+.)+[w-]+(/[w-./?%&=]*)?$ 手机号码: ^1d{10}$ 身份证号: ^(d{15}|d{17}(d|X))$ 中国邮政编码: [1-9]d{5}(?!d) (邮政编码为6位数字)
密码验证
密码验证是常见的需求,一般来说,常规密码大致会满足规律: 6-16位,字母,字符至少包含两种,同时不能包含中文和空格.
如下便是常规密码验证的正则描述:
var reg = /(?!^[0-9]+$)(?!^[A-z]+$)(?!^[^A-z0-9]+$)^[^\s\u4e00-\u9fa5]{6,16}$/;
贪婪模式与非贪婪模式
默认情况下,所有的限定词都是贪婪模式,表示尽可能多的去捕获字符; 而在限定词后增加?,则是非贪婪模式,表示尽可能少的去捕获字符. 如下:
var str = "aaab",reg1 = /a+/,//贪婪模式
reg2 = /a+?/;//非贪婪模式
console.log(str.match(reg1)); //["aaa"],由于是贪婪模式,捕获了所有的a
console.log(str.match(reg2)); //["a"],由于是非贪婪模式,只捕获到第一个a
实际上,非贪婪模式非常有效,特别是当匹配html标签时. 比如匹配一个配对出现的div,方案一可能会匹配到很多的div标签对,而方案二则只会匹配一个div标签对.
var str = "<div class='v1'><div class='v2'>test</div><input type='text'/></div>"; var reg1 = /<div.*<\/div>/; //方案一,贪婪匹配 var reg2 = /<div.*?<\/div>/;//方案二,非贪婪匹配 console.log(str.match(reg1));//"<div class='v1'><div class='v2'>test</div><input type='text'/></div>" console.log(str.match(reg2));//"<div class='v1'><div class='v2'>test</div>"
区间量词的非贪婪模式
一般情况下,非贪婪模式,我们使用的是 “*?”,或 “+?” 这种形式,还有一种是 “{n,m}?”.
区间量词”{n,m}” 也是匹配优先,虽有匹配次数上限,但是在到达上限之前,它依然是尽可能多的匹配,而 “{n,m}?” 则表示在区间范围内,尽可能少的匹配.
需要注意的是:
能达到同样匹配结果的贪婪与非贪婪模式,通常是贪婪模式的匹配效率较高.
分组
正则的分组主要通过小括号来实现,括号包裹的子表达式作为一个分组,括号后可以紧跟限定词表示重复次数.
如下,小括号内包裹的abc便是一个分组:
/(abc)+/.test("abc123") == true
那么分组有什么用呢? 一般来说,分组是为了方便的表示重复次数,除此之外,还有一个作用就是用于捕获,.
捕获性分组
捕获性分组,通常由一对小括号加上子表达式组成. 捕获性分组会创建反向引用,每个反向引用都由一个编号或名称来标识,js中主要是通过
$+编号
或者\+编号
表示法进行引用.
var color = "#808080";
var output = color.replace(/#(\d+)/,"$1"+"~~");//自然也可以写成 "$1~~"
console.log(RegExp.$1);//808080
console.log(output);//808080~~
以上,(\d+)
表示一个捕获性分组,"RegExp.$1"
指向该分组捕获的内容. $编号
这种引用通常在正则表达式之外使用. \编号
这种引用却可以在正则表达式中使用,可用于匹配不同位置相同部分的子串.
var url = "www.google.google.com";
var re = /([a-z]+)\.\1/;
console.log(url.replace(re,"$1"));//"www.google.com"
以上,相同部分的”google”字符串只被替换一次.
非捕获性分组
var color = "#808080";
var output = color.replace(/#(?:\d+)/,"$1"+"~~");
console.log(RegExp.$1);//""
console.log(output);//$1~~
"#808080".match(/#(?:\d+)/); //["#808080"]
以上,
(?:d+)
表示一个非捕获性分组,由于分组不捕获任何内容,所以,RegExp.$1
就指向了空字符串. 同时,由于$1
的反向引用不存在,因此最终它被当成了普通字符串进行替换.
正则表达式高阶技能-零宽断言(环视)
零宽断言,又叫环视. 环视只进行子表达式的匹配,匹配到的内容不保存到最终的匹配结果,由于匹配是零宽度的,故最终匹配到的只是一个位置.
环视按照方向划分,有顺序和逆序两种(也叫前瞻和后瞻),按照是否匹配有肯定和否定两种,组合之,便有4种环视. 4种环视并不复杂,如下便是它们的描述.
(?:pattern) 非捕获性分组,匹配pattern的位置,但不捕获匹配结果.也就是说不创建反向引用,就好像没有括号一样.
(?=pattern) 顺序肯定环视,匹配后面是pattern 的位置,不捕获匹配结果.
(?!pattern) 顺序否定环视,匹配后面不是 pattern 的位置,不捕获匹配结果.
(?<=pattern) 逆序肯定环视,匹配前面是 pattern 的位置,不捕获匹配结果.
(?<!pattern) 逆序否定环视,匹配前面不是 pattern 的位置,不捕获匹配结果.
非捕获性分组由于结构与环视相似,故列在表中,以做对比. 以上4种环视中,目前 javaScript 中只支持前两种,也就是只支持 顺序肯定环视 和 顺序否定环视.
顺序肯定环视:
'Windows(?=2000)'
匹配 "Windows2000" 中的 "Windows"; 不匹配 "Windows3.1" 中的 "Windows"
顺序否定环视:
'Windows(?!2000)'
匹配 "Windows3.1" 中的 "Windows"; 不匹配 "Windows2000" 中的 "Windows"
实例:
var str = "123abc789",s;
//没有使用环视,abc直接被替换
s = str.replace(/abc/,456);
console.log(s); //123456789
//使用了顺序肯定环视,捕获到了a前面的位置,所以abc没有被替换,只是将3替换成了3456
s = str.replace(/3(?=abc)/,3456);
console.log(s); //123456abc789
//使用了顺序否定环视,由于3后面跟着abc,不满意条件,故捕获失败,所以原字符串没有被替换
s = str.replace(/3(?!abc)/,3456);
console.log(s); //123abc789
正则表达式之一 :exec 方法
用正则表达式模式在字符串中查找,并返回该查找结果的第一个值(数组),如果匹配失败,返回null。
rgExp.exec(str)
参数
- rgExp
必选项。包含正则表达式模式和可用标志的正则表达式对象。 - str
必选项。要在其中执行查找的 String 对象或字符串文字。
返回数组
注意:在匹配后,rgExp 的 lastIndex 属性被设置为匹配文本的最后一个字符的下一个位置。lastIndex并不在返回对象的属性中,而是正则表达式对象的属性。
例子1:不含子表达式的正则表达式exec方法循环应用
!function RegExpTest(){
var src="http://sumsung753.blog.163.com/blog/I love you!";
// 注意g将全文匹配,不加将永远只返回第一个匹配。
var re = /\w+/g;
var arr;
// exec使arr返回匹配的第一个,while循环一次将使re在g作用寻找下一个匹配。
while((arr = re.exec(src)) !=null){
document.write(arr.index + "-" + re.lastIndex + ":" + arr + "<br/>");
for(key in arr){
document.write(key + "=>" + arr[key] + "<br/>");
}
document.write("<br/>");
}
}()
结果:
0-4:http
0=>http
index=>0
input=>http://sumsung753.blog.163.com/blog/I love you!
7-17:sumsung753
0=>sumsung753
index=>7
input=>http://sumsung753.blog.163.com/blog/I love you!
18-22:blog
0=>blog
index=>18
input=>http://sumsung753.blog.163.com/blog/I love you!
23-26:163
0=>163
index=>23
input=>http://sumsung753.blog.163.com/blog/I love you!
27-30:com
0=>com
index=>27
input=>http://sumsung753.blog.163.com/blog/I love you!
31-35:blog
0=>blog
index=>31
input=>http://sumsung753.blog.163.com/blog/I love you!
36-37:I
0=>I
index=>36
input=>http://sumsung753.blog.163.com/blog/I love you!
38-42:love
0=>love
index=>38
input=>http://sumsung753.blog.163.com/blog/I love you!
43-46:you
0=>you
index=>43
input=>http://sumsung753.blog.163.com/blog/I love you!
exec默认只返回匹配结果的第一个值,比如上例如果不用while循环,将只返回
'http'
(尽管后面的sumsung753等都符合表达式),无论re表达式用不用全局标记g。但是如果为正则表达式设置了全局标记g,exec从上次匹配结束的位置开始查找。如果没有设置全局标志,exec依然从字符串的起始位置开始搜索。
利用这个特点可以反复调用exec遍历所有匹配,等价于match具有g标志。当然,如果正则表达式忘记用g,而又用循环(比如:while、for等),exec将每次都循环第一个,造成死循环。
如果正则表达式中包含子表达式,那么输出结果将包含子匹配项
例子2:包含子表达式的正则表达式exec方法应用
!function execDemo(){
var r,re; // 声明变量。
var s = "The rain in Spain falls mainly in the plain";
re = /[\w]*(ai)n/ig;
r = re.exec(s);
document.write(r + "<br/>");
for(key in r){
document.write(key + "-" + r[key] + "<br/>");
}
}()
//输出结果如下:
rain,ai
0-rain
1-ai
index-4
input-The rain in Spain falls mainly in the plain
例子3:包含子表达式的正则表达式exec方法循环应用
场景回顾
获取html片段
假如现在,js 通过 ajax 获取到一段 html 代码如下:
var responseText = "<div data='dev.xxx.txt'></div><img src='dev.xxx.png' />";
现我们需要替换img标签的src 属性中的 “dev”字符串 为 “test” 字符串.
① 由于上述 responseText 字符串中包含至少两个子字符串 “dev”,显然不能直接 replace 字符串 “dev”为 “test”.
② 同时由于 js 中不支持逆序环视,我们也不能在正则中判断前缀为 “src=’”,然后再替换”dev”.
如下:
var reg = /dev(?=[^']*png)/; //为了防止匹配到第一个dev,通配符前面需要排除单引号或者是尖括号
var str = responseText.replace(reg,"test");
console.log(str);//<div data='dev.xxx'></div><img src='test.xxx.png' />
当然,以上不止顺序肯定环视一种解法,捕获性分组同样可以做到. 那么环视高级在哪里呢? 环视高级的地方就在于它通过一次捕获就可以定位到一个位置,对于复杂的文本替换场景,常有奇效,而分组则需要更多的操作.
千位分割符
千位分隔符,顾名思义,就是数字中的逗号. 参考西方的习惯,数字之中加入一个符号,避免因数字太长难以直观的看出它的值. 故而数字之中,每隔三位添加一个逗号,即千位分隔符.
var str = "1234567890";
(+str).toLocaleString();//"1,234,567,890"
如上,toLocaleString() 返回当前对象的”本地化”字符串形式.
- 如果该对象是Number类型,那么将返回该数值的按照特定符号分割的字符串形式.
- 如果该对象是Array类型,那么先将数组中的每项转化为字符串,然后将这些字符串以指定分隔符连接起来并返回.
toLocaleString 方法特殊,有本地化特性,对于天朝,默认的分隔符是英文逗号. 因此使用它恰好可以将数值转化为千位分隔符形式的字符串. 如果考虑到国际化,以上方法就有可能会失效了.
我们尝试使用环视来处理下.
function thousand(str){
return str.replace(/(?!^)(?=([0-9]{3})+$)/g,',');
}
console.log(thousand(str));//"1,890"
console.log(thousand("123456"));//"123,456"
console.log(thousand("1234567879876543210"));//"1,879,876,543,210"
上述使用到的正则分为两块. (?!^) 和 (?=([0-9]{3})+$)
. 我们先来看后面的部分,然后逐步分析之.
"[0-9]{3}" 表示连续3位数字.
"([0-9]{3})+" 表示连续3位数字至少出现一次或更多次.
"([0-9]{3})+$" 表示连续3的正整数倍的数字,直到字符串末尾.
那么 (?=([0-9]{3})+$) 就表示匹配一个零宽度的位置,并且从这个位置到字符串末尾,中间拥有3的正整数倍的数字.
正则表达式使用全局匹配g,表示匹配到一个位置后,它会继续匹配,直至匹配不到.
将这个位置替换为逗号,实际上就是每3位数字添加一个逗号.
当然对于字符串"123456"这种刚好拥有3的正整数倍的数字的,当然不能在1前面添加逗号. 那么使用 (?!^) 就指定了这个替换的位置不能为起始位置.
千位分隔符实例,展示了环视的强大,一步到位.
正则实现模板引擎
var tmp = "An ${a} a ${b} keeps the ${c} away";
var obj = {
a:"apple",b:"day",c:"doctor"
};
function tmpl(t,o){
return t.replace(/\${(.)}/g,function(m,p){
console.log('m:'+m+' p:'+p);
return o[p];
});
}
tmpl(tmp,obj);
//结果:
m:${a} p:a
m:${b} p:b
m:${c} p:c
"An apple a day keeps the doctor away"
正则表达式在H5中的应用
H5中新增了 pattern 属性,规定了用于验证输入字段的模式,pattern的模式匹配支持正则表达式的书写方式. 默认 pattern 属性是全部匹配,即无论正则表达式中有无 “^”,“$” 元字符,它都是匹配所有文本.
注: pattern 适用于以下 input 类型:text,search,url,telephone,email 以及 password. 如果需要取消表单验证,在form标签上增加 novalidate 属性即可.
回文字符串
function huiwen(str){
str = str.replace(/[\W\s_]/gi,'');
return str.toLowerCase().split('').reverse().join('') == str.toLowerCase();
}
console.log(huiwen(' a bdba'));
console.log(huiwen(' a bdbc'));
trim去除首尾空格
function _trim(str){
return str.replace(/^\s+|\s+$/,'');
}
//测试
console.log(_trim(' a bdba'));
console.log(_trim('ia bdba'));
将所有单词首字母大写
name = 'aaa bbb ccc';
uw=name.replace(/\b\w+\b/g,function(word){
return word.substring(0,1).toUpperCase()+word.substring(1);}
);
颜色字符串转换
将 rgb 颜色字符串转换为十六进制的形式,如 rgb(255,255,255) 转为 #ffffff
1. rgb 中每个,后面的空格数量不固定
2. 十六进制表达式使用六位小写字母
3. 如果输入不符合 rgb 格式,返回原始输入
示例1
输入: ‘rgb(255,255)’ 输出:#ffffff
function rgb2hex(sRGB) {
return sRGB.replace(/^rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)$/,function($0,$1,$2,$3){
return '#'+toHex($1)+toHex($2)+toHex($3);
});
}
function toHex(str){
return ('0'+(+str).toString(16)).slice(-2);
}
将字符串转换为驼峰格式
css 中经常有类似 background-image 这种通过 - 连接的字符,通过 javascript 设置样式的时候需要将这种样式转换成 backgroundImage 驼峰格式,请完成此转换功能
1. 以 - 为分隔符,将第二个起的非空单词首字母转为大写
2. -webkit-border-image 转换后的结果为 webkitBorderImage
示例1
输入 ‘font-size’
输出 fontSize
function cssStyle2DomStyle(sName) {
//使用正则将 前一位有-的字符替换为大写【-([a-z])】
//replace第二个参数为函数时:
//函数的第一个参数是匹配模式的字符 【t】
//接下来的参数是与模式中的子表达式匹配的字符,可以有0个或多个这样的参数。【m】
//接下来的参数是一个整数,代表匹配在被替换字符中出现的位置【i】
//最后一个参数是被替换字符本身【这里没有用到】
return sName.replace(/-([a-z])/g,function(t,m,i){return i?m.toUpperCase():m;})
}
将Doe,John替换为John,Doe
var name = "Doe,John";
var n = name.replace(/(\w+)\s*,\s*(\w+)/,"$2,$1");
//John,Doe
img标签匹配
var str = `<img src="http://image.163.com"></img>`;
var reg=`<img src="[."]*"></img>`;
var reg1=`<img src="[^"]*></img>`;
var reg2=`<img src="[^"]*"></img>`; //正确
var reg3=`<img src="[."]*></img>`;
var arr = str.match(new RegExp(reg2));
console.log(arr);
console.log(arr.length);
参考链接:
完整的正则表达式:
http://www.jb51.cc/article/p-seawhpfj-d.html
exec、test等方法介绍:
http://www.jianshu.com/p/fcb648efc3de