在处理数据的合法性的时候,经常会遇到校验或处理字符串,而判断字符串的`条件`无穷多。
任意一个简单的字符判断条件都会让你想个半天, 有什么牛掰的办法`快速解决`这样的问题?
正则绝对是最好的选择,学了它不用再写复杂的字符串判断条件了,而且`通吃各大计算机语言`。
`正则表达式`是使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。
方式一: var reg = new RegExp("正则表达式", "正则修饰符"); 或 var reg = new RegExp(/正则表达式/正则修饰符);
方式二: var reg = /正则表达式/正则修饰符
1.以上两种方式定义的`正则对象`用起来是`等价`的。
2.在js里面比较特别的就是`方式二`,它可以直接使用 `双斜杠`完成正则对象的定义
这样的好处在于不需要转移反斜杠字符了,因为在字符串中两个反斜杠代表一个反斜杠。
3.`推荐使用` 【方式二】 定义正则表达式
var reg = /123/;
console.dir(reg);
class_reg.png
在正则对象里面有这么两个方法用来校验字符串的
fun exec() return 第一个匹配结果以数组形式返回或者 `null`
fun test() return true or false
`注意`:它们两个函数会改变正则对象的`lastIndex`属性, lastIndex匹配不到字符串的时候会回到字符串开始的位置
---------`test()例题`---------return true or false
var reg = /123/g; //如果不写正则修饰符g, 那么默认字符串从左往右匹配到第一个就结束匹配,lastIndex会一直是0.
var str = "123123a";
console.log(reg.test(str)); //true
console.log(reg.lastIndex); // 3
console.log(reg.test(str)); //true
console.log(reg.lastIndex); // 6
console.log(reg.test(str)); //false 字符匹配不到了,lastIndex变为0
console.log(reg.lastIndex); // 0
console.log(reg.test(str)); //true
console.log(reg.lastIndex); // 3 lastIndex又从0变成了3,证明了字符匹配不了lastIndex就从0开始
---------`exec()例题`---------return array or null
console.log(reg.exec(str)); // ["123", index: 0, input: "123123a", groups: undefined]
console.log(reg.lastIndex); // 3
console.log(reg.exec(str)); // ["123", index: 3, input: "123123a", groups: undefined]
console.log(reg.lastIndex); // 6
console.log(reg.exec(str)); // null
console.log(reg.lastIndex); // 0
console.log(reg.exec(str)); // ["123", index: 0, input: "123123a", groups: undefined]
console.log(reg.lastIndex); // 3 同理
新手常见书写错误(想用test或exec函数获取字符串的匹配数量)
`注意`:以下while导致浏览器无响应
while(/a/g.test('abcabc')) { //把正则写在while循环里面,/a/g在这里写不是引用,这就会导致js编译器每次执行都会创建一个新的正则对象,返回值会一直都是true,每个对象里面的lastIndex都是1结束
count++;
console.log(/a/g.lastIndex);// 0... 这里的/a/g和上面的/a/g js编译器并不会识别成同一个对象,不使用引用, 编译器都会每次新建一个
}
正确的书写方式
var reg = /a/g;
while(reg.test('abcabc')){ //使用变量reg引用正则对象
count++;
}
如果你想获得一个字符串正则匹配到的所有参数,个数
使用`字符串对象`里面的`match()`函数就可以了
fun match() return array or null; `使用方式:` 字符串.match(\正则表达式\正则修饰符);
目前在ES6标准中一共有5个修饰符
var str = "abcabcabc";
global => g //表示全局匹配字符串
console.log(str.match(/a/)); //["a", index: 0, input: "abcabcabc", groups: undefined]
console.log(str.match(/a/g)); // ["a", "a", "a"]
ignoreCase => i //忽略大小写
console.log(str.match(/A/)); //null
console.log(str.match(/A/i)); //["a", index: 0, input: "abcabcabc", groups: undefined]
console.log(str.match(/A/gi)); //["a", "a", "a"]
multiline => m //开启(多行匹配:正则里面切割行可以用元字符^ 或者 $, 正则表达式中间写这些符号就可以用到多行匹配)
var str = "\nabc\nabcd"; //注意\n在字符里面是换行,是不会识别成字符对象的
//查找字符以a开头的字符
console.log(str.match(/^a/)); //null
console.log(str.match(/^a/m)); //["a", index: 1, input: "↵abc↵abcd", groups: undefined]
console.log(str.match(/^a/gm)); //["a", "a"]
//查找字符以c结尾的字符串
console.log(str.match(/c$/)); //null
console.log(str.match(/c$/m)); //["c", index: 3, input: "↵abc↵abcd", groups: undefined]
console.log(str.match(/c$/gm)); //["c"]
sticky => y //使用了y的表达式通常与lastIndex的值有联系,lastIndex值是多少,正则就必须从该值开始进行完全匹配,如果不能完全匹配返回值为false
var str = '1\na';
var reg = /a$/my;
reg.lastIndex = 2;
reg.test(str); //断言a是str字符的结尾, str[2] => 'a', true
unicode => u //表示 注意是正则进行转义unicode编码在进行匹配, 加了u会转义正则而不是字符串,字符串和unicode编码 js能自动识别
console.log("\u{68}");//h \u{68}被自动识别成了h,
console.log(/\u{68}/.test("h"));//false
console.log(/\u{68}/u.test("h"))//true 由于{}在正则里面是个元字符,现在\u{62}\具有了双层意义,写上修饰符可以明确正则的含义
console.log("\uD842");//� 乱码了
console.log(/\uD842/u.test("�"));//false
console.log("\uD842\uDFB7");//𠮷 发现\uD842在两个字符串里面表示不同字符,这是由于编码集导致的问题
console.log(/\uD842/.test("\uD842\uDFB7"));//true
console.log(/\uD842/u.test("\uD842\uDFB7"));//false 着时候u声明\uD842是一个字符,而js默认认为\uD842\uDFB7也是一个字符, 所以为false
console.log(/\uD842\uDFB7/u.test("𠮷")); //true
console.log(/\uD842\uDFB7/.test("\uD842\uDFB7")); //true
var reg = /a/g;
有时候我们写个正则表达式不仅仅是需要匹配一个a那么简单,我们需要的是满足字符串 `条件`的表达式
为了更好的演示正则的返回值,正则都将调用String对象的match函数来演示
在正则里面如何编写字符条件呢? if(xxx == xxx)?,先从简单的开始
^ => 这个符号通常与它`后面`的 `字符串`联系起来使用,表示后面指定的字符必须在开头, ^的后面可以是一个字符也可以是多个连续字符, 它与其它的元字符联系起来时会有其它意义
console.log("abcabc".match(/^a/g)); //["a"] ^a的表达的条件是,&str[0] = 'a';字符串必须要以a开头
console.log("abcabc".match(/^ab/g)); //["ab"] ^ab的表达的条件是,&str[0] = 'ab';字符串必须要以ab开头
console.log("\nabcabc".match(/^a/g)); //null &str[0] != 'a';
console.log("\nabcabc\na".match(/^a/gm)); //["a", "a"] //m就相当于有多少个\n给你创建多少个str , 检测所有的str, str[0]的都返回
console.log("bcabc".match(/^a/g)); //null &str[0] != 'a';
$ => 它与^相反,它要求出现在它`前面`的`字符串`必须是字符串的结束,$的前面可以是一个字符也可以是多个连续字符
console.log("abc".match(/c$/g)); //["c"] &str[str.length - reg.source.length] = 'c';
console.log("abc".match(/bc$/g)); //["bc"] &str[str.length - reg.source.length] = 'bc';
console.log("abc".match(/c$/)); //["c", index: 2, input: "abc", groups: undefined]
新手常见理解错误
console.log('abc'.match(/^abc$/g)); //["abc"]
console.log('abc123abc'.match(/^abc$/g)); //null 'abc123abc' != 'abc';
{} => 它要求出现在它`前面`的`单个`字符, 它表示的它前面的字符匹配数量范围,它里面写范围 {n,m} n <= 字符出现次数 <= m
c{1} 表示找出字符串里面所有 &str[lastIndex] = "c";
console.log("cccc".match(/c{1}/g)); //["c", "c", "c", "c"]
c{2} 表示找出字符串里面所有 &str[lastIndex] = "cc";
console.log("cccc".match(/c{2}/g)); //["cc", "cc"]
注意了:你千万不要以为上面打印会返回3个["cc", "cc","cc"] , &str[0] = "cc", &str[1] = "cc", &str[3] = "cc", &str[4] = "c"
`正则找到了符合条件的字符串就不能再次在原有字符串匹配了,下一个匹配的字符串要截掉之前已经匹配了的`
证明:
var str = "cccccc";
var reg = /c{2}/g;
console.log(reg.exec(str)); //["cc", index: 0, input: "cccccc", groups: undefined] index = 0
console.log(reg.exec(str)); //["cc", index: 2, input: "cccccc", groups: undefined] index = 2 index != 1 说明了之前匹配了得截掉了
console.log(reg.exec(str)); //["cc", index: 4, input: "cccccc", groups: undefined] index = 4
c{3}找出字符串里面所有 &str[lastIndex] = "ccc";
console.log("cccc".match(/c{3}/g)); //["ccc"]
c{1,3} 表示找出字符串里面c的次数可以出现大于一次或者小于三次也就是说&str[lastIndex] == "c" || &str[lastIndex] == "cc" || &str[lastIndex] == "ccc"
问题来了"ccc"里面同时符合上面上3个条件,返回啥呢?返回["c", "cc"]吗?还是返回["cc", "c"]?还是返回["ccc"]呢?
console.log("ccc".match(/c{1,3}/g)); //答案是: ["ccc"], 正则优先返回最佳匹配,最好都符合三个条件的
最佳匹配:判断条件发现当前字符串加上下一个字符也满足条件, 会一直加下去,直到满足最后一个条件结束
console.log("cccccccc".match(/c{1,3}/g)); //["ccc", "ccc", "cc"]
c{1,} 表示找出字符串里面&str[lastIndex] == "c~" 只要字符串满足有一个c,或者n个c(n > 1)都返回
console.log("ccc".match(/c{1,}/g)); //["ccc"],返回了最佳匹配的字符串
console.log("c cc ccc cccc ccccc".match(/c{1,}/g)); //["c", "cc", "ccc", "cccc", "ccccc"]
{0,1} 0次或者多次简写为?
{1,} 1次或者多次简写为+
新手常见理解错误
console.log("123123".match(/123{1}/g)); //["123", "123"]
错误理解:找出字符串里面&str[lastIndex] = "123"
正确理解:&str[lastIndex] = "ab" && &str[lastIndex+2] = "3", 是要分两步去理解正则的
{}它是只找出前面的`单个`字符为入参条件,
[] => 它的条件是写在它里面的, 例如[123] 表示&str[lastIndex] == 1 || &str[lastIndex] == 2 || &str[lastIndex] == 3, 里面的每个字符都会拆分成一个条件
console.log("a1b a2b a3b a4d".match(/a[123]b/g)); // ["a1b", "a2b", "a3b"] (&str[0] = "a" && &str[1] = "1-3" && &str[2] == "b")
推出一个判断字符是否是数字的条件[0123456789] 简写[0-9] 再简写\d
推出一个判断字符是否是小写字母的条件[abc...z] 简写[a-z]再简写没找到
推出一个判断字符是否是大写字母的条件[ABC...Z] 简写[A-Z]
推出一个判断字符既符合小写也符合大写字母的条件[abc..zABC...Z] 简写[a-zA-Z]
推出一个判断字符是换行的条件[\n]
但是我要是反过来推呢?要一个不符合是数字的条件呢, 怎么让&str[lastIndex] != `0-9`;
你只要在在[]里面写上一个^,[^123] 判断条件就变成了&str[lastIndex] != 1 || &str[lastIndex] != 2 || &str[lastIndex] != 3
[^0123456789]变成了个判断字符不是数字的条件, 简写[^0-9] 再简写\D
console.log("a1b a2b a3b a4d".match(/a[^123]b/g)); //null
() => 通常它的里面写正则,优先级仅仅次于/符号,/()/相当于正则嵌套正则,()它是有返回值的,可以用来做引用,而且可以与部分元字符联动使用
1.()真的有返回值吗?
console.log("a".match(/[a-z]/)); //["a", index: 0, input: "a", groups: undefined] 只有一个返回值
console.log("a".match(/([a-z])/)); //["a", "a", index: 0, input: "a", groups: undefined] 使用()发现多了一个a,这个多余的a是谁返回的呢,他们的返回顺序究竟是怎么样的呢?
1.1
第二个a的返回值究竟是谁的呢?
console.log("aaa".match(/a([a-z])/)); // ["aa", "a", index: 0, input: "aaa", groups: undefined] 发现aa刚好是a([a-z])的返回值, 而a是([a-z])的返回值, 先返回的最外层的正则结果
1.2
这个()里面的正则返回值最多能存储多少个呢?
var reg = /a([a-z])/g;//注意加了全局匹配之后,会无视所有的匹配组,为啥我也不知道?
console.log("abac".match(reg)); //["ab", "ac"]
([a-z])是不是应该有a和b呢,它们两都能找到吗?
RegExp.$1...$9属性用于返回正则表达式模式中某个`子表达式`匹配的文本。
console.log(RegExp.$1); //c b跑哪里去呢?很明显b是被c给覆盖了, 第一个括号的返回值
console.log(RegExp.$2); //什么都没有 因为这是第二个子正则的返回值,第二个子正则不存在
console.log(RegExp.$9); //什么都没有
console.log(RegExp.$10); //undefine
推断:当正则匹配完后,一个()字表达式只有一个返回值
1.3
这个$1,$2顺序是咋样的啊?
var reg = /a([a-z]([a-z]([a-z])))/g; //这么多括号,$1究竟是最里面的返回值还是最外面这个()的返回值啊
console.log("abac".match(reg));
console.log(RegExp.$1); //bac
console.log(RegExp.$2); //ac
console.log(RegExp.$3); //c
答案是:$1它是最外面的的返回值,从左往右执行
1.4
()返回值的引用
var reg = /a([a-z])\1/; // \1是啥意思呢,是RegExp.$1第一个括号的返回值
console.log("abbc".match(reg)); // ["abb", "b", index: 0, input: "abbc", groups: undefined]
var reg2 = /a(?<HAO>[a-z])/; //?<HAO>是啥意思,相当于给你的组取名字,别名
console.log("abbc".match(reg2)); //["ab", "b", index: 0, input: "abbc", groups: {HAO:"b"}]
console.log("abbc".match(reg2).groups.HAO);//打印组的内容b
1.5引用这么用
reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
console.log('2017-11-21'.replace(reg, '$<month>/$<day>/$<year>'));//11/21/2017, 在replace里面你可以$<month>来对应引用正则对应别名
1.6可有可无的组引用(非捕获型分组)
var html_rule = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;//这是一个匹配html标签的正则
console.log("<div></div>".match(html_rule)); //["<div></div>", "div", index: 0, input: "<div></div>", groups: undefined]
console.log(RegExp.$1); //div
console.log(RegExp.$2); //什么都没有
console.log("<br/>".match(html_rule)); //["<br/>", "br", index: 0, input: "<br/>", groups: undefined]
console.log(RegExp.$1); //br
console.log(RegExp.$2); //什么都没有
// ^<(\w+) 匹配一个以字母数字下划线且长度必须大于等于1的为开头的 <xxx
// \s*\/?> 匹配呢一个空格(\s)和任意长度的字符(*)\/?>匹配 >结尾或者是 />结尾(?匹配前面一个字符0次或者一次)
// ?: 在组的前面这样写表示这个条件可以无视, 但是如果有对应的匹配就必须符合组里面的条件,这样的组是没有返回值的,你在里面写个?key是会报错的
// (?:<\/\1>|)$ (\1引用转换) => <\ (\w+)/> 必须是</xxx>结尾的或者没有结尾
还有几个非捕获型分组,笔者就不一一阐述了,笔者也很少用到。