javascript正则表达式

\color{#ea4335}{1.正则表达式有什么用,是什么?}
在处理数据的合法性的时候,经常会遇到校验或处理字符串,而判断字符串的`条件`无穷多。

任意一个简单的字符判断条件都会让你想个半天, 有什么牛掰的办法`快速解决`这样的问题?

正则绝对是最好的选择,学了它不用再写复杂的字符串判断条件了,而且`通吃各大计算机语言`。

`正则表达式`是使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。
\color{#ea4335}{2.正则表达式的定义方式}
方式一:  var reg = new RegExp("正则表达式", "正则修饰符"); 或 var reg = new RegExp(/正则表达式/正则修饰符);

方式二:  var reg = /正则表达式/正则修饰符


1.以上两种方式定义的`正则对象`用起来是`等价`的。

2.在js里面比较特别的就是`方式二`,它可以直接使用 `双斜杠`完成正则对象的定义
这样的好处在于不需要转移反斜杠字符了,因为在字符串中两个反斜杠代表一个反斜杠。

3.`推荐使用` 【方式二】 定义正则表达式
\color{#ea4335}{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(\正则表达式\正则修饰符);
\color{#ea4335}{4.解读正则修饰符}
目前在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
\color{#ea4335}{5.解读元字符}
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>结尾的或者没有结尾


还有几个非捕获型分组,笔者就不一一阐述了,笔者也很少用到。







最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容