元字符
[拥有特殊含义的元字符]
\d -> 匹配一个0-9的数字,相当于[0-9],和它相反的是\D ->匹配一个除了0-9的任意字符
\w -> 匹配一个0-9、a-z、A-Z、_的数字或字符,相当于[0-9a-zA-Z_]
\s -> 匹配一个空白字符(空格、制表符...)
\b -> 匹配一个单词的边界
\t -> 匹配一个制表符
\n -> 匹配一个换行
. -> 匹配一个除了\n以外的任意字符
^ -> 以某一个元字符开头
$ -> 以某一个元字符结尾
\ -> 转移字符
x|y -> x或者y的一个
[xyz] -> x、y、z中的任意一个
[^xyz] -> 除了xyz中的任意一个字符
[a-z] -> 匹配a-z中的任意一个字符
[^a-z] -> 匹配除了a-z中的任意一个字符
() -> 正则中的分组
注意:
1)关于[]
a、[+] ->中括号中出现的所有字符都代表的是本身的意思
b、[12-65] ->这个不是12-65而是1/2-6/5三者中的一个
- 关于()
a、分组的作用是改变默认的优先级,例如:/^18|19$/,181、189、119、819、1819...都符合,而不是我们认为的18或19,但是改成/^(18|19)$/就是单纯的18或19了
b、可以在捕获大正则匹配的内容同时,把分组匹配的内容也进行捕获->分组捕获
c、分组引用,例如:/^(\d)(\w)\2\1$/,这里的\2是和第二个分组出现一模样的内容,\1是和第一个分组出现一模一样的内容,例如:"0aa0"就符合了
[代表数量的量词元字符]
* -> 0到多个
+ -> 1到多个
? -> 0到1个
{n} -> 出现n次
{n,} -> 出现n到多次
{n,m} -> 出现n到m次
注意:
1)关于?的几种情况
a、放在非量词元字符后面,代表出现0-1次
b、放在量词元字符后面,代表取消捕获时候的贪婪性,例如:reg=/\d+/; reg.exec("2015") -> "2015" 但是如果正则这样写 reg=/\d+?/; reg.exec("2015") -> "2"
c、在分组开头加?:,代表当前的分组只是匹配不进行捕获,例如:/^(?:\d+)$/
d、在分组开头加?=,正向预查,例如:/^zhufeng(?=1|2)$/ 只有"zhufeng1"和"zhufeng2"才符合
e、在分组开头加?!,负向预查,例如:/^zhufeng(?!1|2)$/ 除了"zhufeng1"和"zhufeng2"不符合,其他的只要是"zhufeng(任何的东西)"都符合
[代表本身意思的元字符]
除了以上的,在字面量方式中,我们出现的其他任意字符代表的都是自己本身的意思
var num=12;
var reg=/^\w"+num+"$/; ->这里"+num+"不是把变量的值拼接,而这里的不管是"还是+都是元字符
->对于需要拼接字符串和变量的方式我们只能使用实例方式创建正则
修饰符
i -> ignoreCase 忽略字母的大小写
g -> global 全局匹配 (加上g可以解决正则捕获时候的懒惰性)
m -> multiline 多行匹配
项目中常用的正则
1)有效数字的
var reg=/^[+-]?(\d|([1-9]\d+))(\.\d+)?$/;
2)邮箱的
var reg = /^\w+((-\w+)|(\.\w+))*@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/;
3)电话的
var reg = /^1\d{10}$/;
4)年龄在18-65之间的
var reg = /^((18|19)|([2-5]\d)|(6[0-5]))$/;
5)中文姓名
var reg = /^[\u4e00-\u9fa5]{2,4}$/;
6)身份证
var reg = /^(\d{6})(\d{4})(\d{2})(\d{2})(?:\d{2})(\d)(?:\d|X)$/;
//-> 130828(省市县) 1990(年) 12(月) 04(日) 06 1(奇数是男偶数是女) 7(数字或者X)
正则的匹配
reg.test([string]) ->true就是匹配成功 false->匹配不成功
正则的捕获
1、exec:正则的捕获方法 ->先匹配,然后把匹配的内容进行捕获
1)如果字符串没有匹配这个正则,捕获的结果是null
2)如果和正则匹配,返回的结果是一个数组
["2015", index: 7, input: "zhufeng2015peixun2016"]
第一项是我们捕获的内容
index:捕获的内容在元字符串中开始的索引位置
input:捕获的原始字符串
2、正则的捕获是懒惰的
原理:正则的每一次捕获都从lastIndex值开始,第一次捕获的时候,lastIndex=0,从原始字符串索引为0的位置开始查找捕获;而默认的情况下,第一次捕获完成,lastIndex的值并没有发生改变,还是0,所以第二次捕获还是从原始字符串索引为0处开始查找,这样找到的还是第一次捕获的内容...
解决办法:添加全局修饰符g ->加上g后,第一捕获完成后,lastIndex值发生了改变,变为第一次捕获内容后的第一个字符的开始索引,第二次捕获是继续向后查找的...
疑问:我不用全局修饰符g,每一次捕获完成手动的修改lastIndex值行吗? ->不行,虽然手动的修改lastIndex,确实把lastIndex进行改变了,但是正则查找的时候还是从索引0开始的
var str = "zhufeng2015peixun2016";
var reg = /\d+/g;
为了防止没有加全局修饰符g导致的死循环,我们在处理之前,对于没有添加g的手动给添加一个g
RegExp.prototype.myExecAll = function myExecAll() {
var _this = this, str = arguments[0], ary = [], res = null;
!_this.global ? _this = eval(_this.toString() + "g") : null;
res = _this.exec(str);
while (res) {
ary[ary.length] = res[0];
res = _this.exec(str);
}
return ary;
};
console.log(reg.lastIndex);//->0
var res = reg.exec(str);
console.log(res);
console.log(reg.lastIndex);//->11
res = reg.exec(str);
console.log(res);
console.log(reg.lastIndex);//->21
res = reg.exec(str);
console.log(res);//->null
3、字符串中存在一个叫做match的方法也可以实现捕获,而且只要我们取消了正则的懒惰型,执行一次match方法就可以捕获到所有的内容了
var str = "zhufeng2015peixun2016";
var reg = /\d+/g;
console.log(str.match(reg));
疑问:那我们都用match来替换exec多好啊?
4、正则的分组捕获
每一次捕获的时候,不仅仅可以把大正则匹配的内容捕获到,而且还可以把每一个小分组(子正则)匹配的内容单独的捕获到
var str = "zhufeng[2015]peixun[2016]";
var reg = /\[(\d)(\d+)\]/g;
var res = reg.exec(str);
console.log(res);
["[2015]", "2", "015", index: 7, input: "zhufeng[2015]peixun[2016]"]
数组中的第一项是大正则捕获的内容 res[0]
数组中的第二项是第一个分组捕获的内容 res[1]
数组中的第三项是第二个分组捕获的内容 res[2]
....
分组的只匹配不捕获:我们如果执行把分组内容进行匹配但是不进行捕获的话,只需要在分组的前面加上?:即可
var str = "zhufeng[2015]peixun[2016]";
var reg = /\[(?:\d)(\d+)\]/g;
var res = reg.exec(str);
console.log(res);
["[2015]", "015"...]
数组中的第一项是大正则捕获的内容 res[0]
数组中的第二项是第二个分组捕获的内容 res[1]
第一个分组添加了?:,所以只匹配不捕获
5、match和exec的区别:
match只能捕获到大正则匹配的内容,对于有分组的情况下,不能捕获分组的内容;所以,如果捕获的时候不需要捕获分组的内容,我们直接的使用match更加的方便,如果需要捕获分组的内容,我们只能使用exec来一个个的捕获
var str = "zhufeng[2015]peixun[2016]";
var reg = /\[(\d+)\]/g;
//console.log(str.match(reg));//->["[2015]", "[2016]"]
var ary = [];
var res = reg.exec(str);
while (res) {
//ary.push(res[1]);
ary.push(RegExp.$1);//RegExp.$1获取当前正则第一个分组捕获的内容(可能在部分IE浏览器中获取不到值)
res = reg.exec(str);
}
console.log(ary);
6、正则的贪婪性:在每一次捕获的时候,总是按照正则匹配的最长结果捕获
var str = "zhufeng2015peixun2016";
var reg = /\d+/g;
console.log(reg.myExecAll(str));//-->["2015","2016"]
var str = "zhufeng2015peixun2016";
var reg = /\d+?/g;
console.log(reg.myExecAll(str));//-->["2", "0", "1", "5", "2", "0", "1", "6"]
7、分组引用
\2代表出现和第二个分组一模一样的内容
\1代表出现和第一个分组一幕一样的内容
var reg=/^(\w)(\w)\2\1$/;
"woow"、"1221"...
8、字符串方法->replace:把一个字符串中的某个字符串替换成新的内容
[在不使用正则的情况下]
每一次执行只能替换一个,需要替换多个,同样需要执行多次
var str = "zhufeng2015 zhufeng2016";
"zhufeng" -> "珠峰"
str = str.replace("zhufeng", "珠峰").replace("zhufeng", "珠峰");
有些时候即使执行多次,也实现不了替换
"zhufeng" -> "zhufengpeixun"
str = str.replace("zhufeng", "zhufengpeixun").replace("zhufeng", "zhufengpeixun");
[第一个参数可以是一个正则] 把所有和正则匹配的内容进行替换(但是和捕获一样默认是懒惰的,只有加上全局修饰符g才可以)
var str = "zhufeng2015 zhufeng2016";
str = str.replace(/zhufeng/g, "zhufengpeixun");
console.log(str);
1)执行和执行次数问题
其实和exec捕获的原理是一模一样的
例如:我们第二个参数如果传递的是一个函数,每当正则在字符串中捕获一次当前的这个函数就执行一次 ->本题中一共捕获了两次,所以函数执行了两次
var str = "zhufeng2015 zhufeng2016";
str = str.replace(/zhufeng/g, function () {
2)参数问题
console.dir(arguments);
不仅仅是执行function,而且还给我们的函数传递了参数,并且传递的参数和每一次exec捕获的内容是一模一样的
如果是第一次exec捕获->["zhufeng",index:0,input:"原始字符串"]
第一次执行函数里面的参数
arguments[0] -> "zhufeng"/**/
arguments[1] -> 0 相当于exec中的index 开始捕获的索引位置
arguments[2] -> "原始字符串" 相当于exec中的input
3)返回值问题
return返回的是什么,就相当于把当前捕获的内容替换成什么
return "zhufengpeixun";
});
实战
1、获取一个字符串中出现次数最多的字符和对应的次数,例如:var str = "zhufengpeixun2015yangfanqihang";问哪几个字符出现的次数最多,一共有多少次?
var str = "zhufengpeixun2015yangfanqihang", obj = {};
//首先统计每一个字符出现的次数
str.replace(/\w/g, function () {
var key = arguments[0];
if (obj[key] >= 1) {
obj[key]++;
return;
}
obj[key] = 1;
});
//用假设法获取出现的最多次数
var maxNum = 0;
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
obj[key] > maxNum ? maxNum = obj[key] : null;
}
}
//最后在把所有出现次数为maxNum的输出
var res = "最多出现:" + maxNum + "次 出现的字符是:";
for (key in obj) {
if (obj.hasOwnProperty(key)) {
obj[key] === maxNum ? res += key + " " : null;
}
}
obj = null;
console.log(res);
2、模板引擎绑定数据实现的原理
//->"my name is 葛琪,my age is 26,i com from china,i can do javascript~~";
var ary = ["葛琪", "26", "china", "javascript"];
str = "my name is {0},my age is {1},i com from {2},i can do {3}~~";
str = str.replace(/\{(\d+)\}/g, function () {
return ary[arguments[1]];
});
console.log(str);
3、单词首字母大写
str = "my name is zhou xiao tian,my age is twenty five years old~~";
str = str.replace(/\w+/g, function () {
var res = arguments[0];
return res.substr(0, 1).toUpperCase() + res.substr(1);
});
console.log(str);
4、格式化时间
->"2015年05月03日 12时09分13秒"
str = "2015-05-03 12:09:13";
str = str.replace(/^(\d+)-(\d+)-(\d+) +(\d+):(\d+):(\d+)$/, "$1年$2月$3日 $4时$5分$6秒");
console.log(str);
String.prototype.myFormatTime = function myFormatTime() {
var reg = /^(\d{4})(?:-|\/|\.|:)(\d{1,2})(?:-|\/|\.|:)(\d{1,2})(?:\s+)(\d{1,2})(?:-|\/|\.|:)(\d{1,2})(?:-|\/|\.|:)(\d{1,2})$/g, ary = [];
this.replace(reg, function () {
ary = Array.prototype.slice.call(arguments, 1, 7);
});
var format = arguments[0] || "{0}年{1}月{2}日 {3}:{4}:{5}";
return format.replace(/{(\d+)}/g, function () {
var val = ary[arguments[1]];
return val.length === 1 ? "0" + val : val;
});
};
str = "2015-5-3 12:9:13";
console.log(str.myFormatTime());
console.log(str.myFormatTime("{0}年{1}月{2}日"));
console.log(str.myFormatTime("{1}-{2} {3}:{4}"));
5、数字转大写
//->"贰零壹伍"
str = "2015";
ary = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
str = str.replace(/\d+?/g, function () {
return ary[arguments[0]];
});
console.log(str);
6、queryURLParameter 获取地址栏中的参数
->http://kbs.sports.qq.com/kbsweb/game.htm?mid=1467588&age=18
->{mid:"1467588",age:18}
String.prototype.queryURLParameter = function queryURLParameter() {
var reg = /([^?&=]+)=([^?&=]+)/g, obj = {};
this.replace(reg, function () {
obj[arguments[1]] = arguments[2];
});
var res = reg.exec(this);
while (res) {
obj[RegExp.$1] = RegExp.$2;
res = reg.exec(this)
}
return obj;
};
var url = window.location.href;//获取当前页面的地址,下面我们假设一个地址,来看一下方法编写的正确性
url = "http://kbs.sports.qq.com/kbsweb/game.htm?mid=1467588&age=18";
console.log(url.queryURLParameter());