从零开始学正则(七:终章)

 壹 ? 引

花了差不多半个月的晚上时间,正则入门学习也步入尾声了,当然正则的学习还将继续。不得不说学习成效非常明显,已能看懂大部分正则以及写出不太复杂的正则,比如帮组长写正则验证文件路径正确性,再如进产品页根据页面地址获取产品id:

let pathname = ‘/webtoprint/dynamicsize-gamebox-2033986.html‘;
let productId = pathname.match(/\-(\d+)\./)[1]; //2033986

虽然正则都不难,但是相比之前使用split各种切切切真的要舒服的多,想到这一点就开心。所以如果有缘的你看到这篇作为结束的文章,还是非常推荐静下心花点闲余时间学习正则。

那么在最终章,我们将正则与API结合起来,真正把正则用起来,毕竟学了不用总会忘,忘了再学就巨亏。说在前面,正则学习系列文章均为我阅读老姚《JavaScript正则迷你书》的读书笔记,文中所有正则图解均使用regulex制作。那么本文开始!

 贰 ? 正则的四种操作

我们知道正则是一种匹配模式,要么匹配字符,要么匹配位置,正则的核心用处就是在于匹配我们预期的东西。那么拿到了想要的东西能做什么?其实无非就是用于验证,切分,提取以及替换。比如文章开头匹配产品id就是提取我们想要的东西,再如千位分隔符就是将数字替换成我们想要的样子,其实也不难理解,我们来细说这四种操作。

1.验证

谈到验证首先想到的就是表单验证,使用正则验证用户输入字段是否符合规范,比如验证数字密码长度是否为六位,最常用的就是使用test方法:

var regex = /^\d{6}$/;
regex.test(123456); //true
regex.test(12345); //false
regex.test(1234567); //false

除了字符的完整比较验证,有时候我们也需要验证字符中是否包含某个特定字符,这种情况下除了test,我们能用match,search,exec这三个方法来做,比如验证字符中是否包含 ? :

可以使用 test 方法:

var regex = /?/;
regex.test(‘123?‘); //true
regex.test(‘a?b‘); //false
regex.test(‘echo‘); //false

使用match方法,此时返回并非布尔值,而是匹配结果,若要判断有没有只需对比返回值是否等于null即可:

var regex = /?/;
‘123?‘.match(regex); //["?", index: 3, input: "123?", groups: undefined]
‘a?b‘.match(regex); //["?", index: 1, input: "a?b", groups: undefined]
‘echo‘.match(regex); //null

使用search方法,此时返回第一个符合条件字符的索引,若没找到返回-1,所以判断有没有对比是否等于-1即可:

var regex = /?/;
‘?123?‘.search(regex); //0
‘a?b‘.search(regex); //1
‘echo‘.search(regex); //-1

使用exec方法,此方法结果与match类似,判断有没有看是否为null即可:

var regex = /?/;
regex.exec(‘?123?‘); //["?", index: 0, input: "?123?", groups: undefined]
regex.exec(‘a?b‘); //["?", index: 1, input: "a?b", groups: undefined]
regex.exec(‘echo‘); //null

当然如果真的是判断有没有某个字符的需求,其实用 indexOf 就可以了,这里也只是列举这些方法的作用。

2.切分

说到切分自然想到字符串切分的split方法,比如:

var str = ‘hello echo‘;
var a = str.split(" ");
console.log(a, str); //["hello", "echo"] "hello echo"

split方法返回一个切割后的数组,它并不会修改原字符串。其实split接受参数除了字符意外,它还能接受一个正则作为切分条件,比如上面的例子我们也可以写成:

var str = ‘hello echo‘;
var a = str.split(/\s/);
console.log(a, str); //["hello", "echo"] "hello echo"

3.提取

正如文章开头提取产品id的例子一样,正则提取操作非常使用,而提取就依赖于正则分组存储的特性,所以分组记得一定不能使用非捕获型括号,看个简单的例子:

var regex = /(\d{4})\/(\d{2})\/(\d{2})/;
var result = regex.exec(‘2019/12/28‘); //["2019/12/28", "2019", "12", "28", index: 0, input: "2019/12/28", groups: undefined]
console.log(result[1]); //2019
console.log(result[2]); //12
console.log(result[3]); //28
// 或直接通过RegExp对象访问
console.log(RegExp.$1); //2019
console.log(RegExp.$2); //12
console.log(RegExp.$3); //28

4.替换

替换必须依赖字符串replace方法,比如将yyyy/mm/dd修改为yyyy-mm-dd:

var str = ‘yyyy/mm/dd‘;
var result = str.replace(/\//g, ‘-‘);
console.log(result, str); //yyyy-mm-dd yyyy/mm/dd

注意replace返回替换后的字符,并不会修改原字符串。

 叁 ? 正则相关的API使用注意

整合上文四种正则操作可以发现,正则使用的API一共就六个,其中字符串方法四个,正则方法两个:

字符串方法split:负责字符串切分,可使用正则作为切分条件,返回切分后的数组,不修改原字符串。

字符串方法search:根据正则查找并返回第一个符合条件的字符索引,注意是第一个,如果没找到返回-1。

字符串方法match:根据正则匹配符合条件的字符,返回一个数组,如果没找到返回null,此方法与exec类似。

字符串方法relpace:根据正则条件进行字符替换,返回替换完成的字符,不会修改源字符串。

正则方法exec:与match类似,返回一个包含符合条件字符,分组匹配字符等信息的数组,没找到返回null。

正则方法test:验证时常用,验证字符是否有符合正则条件的字符,返回一个Boolean值,有为true,没有返回false。

在使用这些方法时,还是有一些需要注意的点,这里我们做个整合:

1.search与match会将字符参数转为正则

字符串的split,replact,match,search四个方法都接受字符串或者正则作为参数,但在使用时,match与search会将字符串参数转为正则:

var str = ‘2019.12.28‘;
var result1 = str.search(‘.‘); //0
var result2 = str.match(‘.‘); //["2", index: 0, input: "2019.12.28", groups: undefined]

在上面的例子,我们本意是匹配第一个小数点的位置,但这两个方法将小数点转为了正则,也就是通配符,很自然第一个数字2就符合条件,所以search返回了2的索引0,match返回了2。

为了避免这个问题还是建议匹配条件直接使用正则,避免不必要的麻烦,像这样:

var str = ‘2019.12.28‘;
var result1 = str.search(‘\\.‘); //4
var result2 = str.match(/\./); //[".", index: 4, input: "2019.12.28", groups: undefined]

当然如果只是查找某个字符有没有,还是推荐indexOf,不用像上面花里胡哨。

2.match匹配受修饰符g影响

我们在前面说字符串方法match与正则方法exec非常类似,若成功匹配都是返回一个数组,失败返回null,而数组中包含了第一个符合条件的字符,分组捕获字符等信息:

var str = ‘2019.12.28‘;
var result1 = str.match(/\d+/); //["2019", index: 0, input: "2019.12.28", groups: undefined]
var result2 = /\d+/.exec(‘2019.12.28‘); //["2019", index: 0, input: "2019.12.28", groups: undefined] 

可以看到不加g情况两个方法匹配结果完全一样,现在我们加上全局匹配修饰符g再看:

var str = ‘2019.12.28‘;
var result1 = str.match(/\d+/g); //["2019", "12", "28"]
var result2 = /\d+/g.exec(‘2019.12.28‘); //["2019", index: 0, input: "2019.12.28", groups: undefined]

当match方法的正则添加了修饰符g,返回结果将只包含所有符合条件的字符,不再包含字index等信息。而exec方法很明显不受g影响。

3.exec使用修饰符g的妙用

上面说match方法使用了g虽然能拿到所有符合条件的字符,但不知道每次出现的索引信息,exec正好解决了这个问题。

当exec方法使用了修饰符g,exec第一次匹配从索引0开始,之后每次匹配都会从上次匹配失败的索引位置(lastIndex)开始,直至匹配一圈后重置索引为0,也就是开始下一轮匹配:

var str = ‘2019.12.28‘;
var regexp = /\d+/g
console.log(regexp.exec(‘2019.12.28‘), regexp.lastIndex); // ["2019", index: 0, input: "2019.12.28", groups: undefined] 4
console.log(regexp.exec(‘2019.12.28‘), regexp.lastIndex); // ["12", index: 5, input: "2019.12.28", groups: undefined] 7
console.log(regexp.exec(‘2019.12.28‘), regexp.lastIndex); // ["28", index: 8, input: "2019.12.28", groups: undefined] 10
console.log(regexp.exec(‘2019.12.28‘), regexp.lastIndex); // null 0
console.log(regexp.exec(‘2019.12.28‘), regexp.lastIndex); // ["2019", index: 0, input: "2019.12.28", groups: undefined] 4

可以看到在走完第四次匹配后,字符串被完整匹配了一遍,此时lastIndex又被重置为0,接下来又开始新一轮匹配。

4.test也会受修饰符g影响

前面说exec会受g影响,准确来说正则的两个方法都受g影响,另一个方法test也是如此,只要添加了全局修饰符g,正则每次匹配完成都会修改lastIndex,这一点与exec保持一致:

var str = ‘2019.12.28‘;
var regexp = /\d+/g
console.log(regexp.test(‘2019.12.28‘), regexp.lastIndex); // true 4
console.log(regexp.test(‘2019.12.28‘), regexp.lastIndex); // true 7
console.log(regexp.test(‘2019.12.28‘), regexp.lastIndex); // true 10
console.log(regexp.test(‘2019.12.28‘), regexp.lastIndex); // false 0
console.log(regexp.test(‘2019.12.28‘), regexp.lastIndex); //true 4

5.验证字符整体要加^和$

这个在前面几篇文章已经有说过,一般我们验证用户输入表单信息,都是验证用户输入的完整字符是否符合规格,所以要验证整体是否符合,一定得加^和$:

var regexp = /\d{6}/;
console.log(regexp.test(‘1234567‘)); // true
console.log(regexp.test(‘123456‘)); // true

var regexp = /^\d{6}$/;
console.log(regexp.test(‘1234567‘)); // false
console.log(regexp.test(‘123456‘)); // true

比如这个例子中我们要求密码必须是6位数字,如果不加^和$,七位数字也包含了六位数字的情况,所以为true。

6.split你不知道的事

split方法我们在前面说了它接受一个字符或者一个正则作为切分字符的条件,并将字符按此条件切成一个数组并返回。

其实split方法还能接受第二个参数,用于限定返回数组的长度:

var str = ‘听风是风 时间跳跃 行星飞行 echo‘;
console.log(str.split(‘ ‘, 2)); //["听风是风", "时间跳跃"]

第二点是,如果我们使用正则作为切分条件,且正则使用了分组,那么返回的数据将包含分隔符:

var str = ‘听风是风 时间跳跃 行星飞行 echo‘;
console.log(str.split(/(\s)/)); //["听风是风", " ", "时间跳跃", " ", "行星飞行", " ", "echo"]

7.强大的replace方法

我们理解的replace方法第二参数就是自定义字符,最基本的用法:

var str = ‘听风是风 时间跳跃 行星飞行 echo‘;
console.log(str.replace(/\s/g,‘?‘)); //听风是风?时间跳跃?行星飞行?echo

其实正则自身也提供了一部分字符,如下:

属性 描述
$1,$2...$99 匹配第1-99个分组里捕获的文本
$& 匹配到的子串文本
$` 匹配到的子串的左边文本
$‘ 匹配到的子串的右边文本
$$ 美元符号

比如将 2019/12/29 替换成 2019-12-28,可以这么做,这里解释了$1,$2含义:

var str = ‘2019/12/29‘;
console.log(str.replace(/(\d{4})\/(\d{2})\/(\d{2})/g, ‘$1-$2-$3‘)); //2019-12-29

console.log(RegExp.$1); //2019
console.log(RegExp.$2); //12
console.log(RegExp.$3); //29

我们来通过一个例子来解释$&,如下:

var result = "a,b,c".replace(/\w/g, "$&$&");
console.log(result);//aa,bb,cc

很多人看到这里可能就不理解了,这是怎么替换的?记住一点,replace具有遍历特性,前面正则能匹配到几次,后面的替换就会执行几次。

第一次匹配到了a,所以此时的 $&表示 a,执行替换,a被替换成了 aa。

第二次匹配到了b,此时的$&表示 b,又执行替换,此时b又变成了bb,以此类推,经历过三次替换于是变成了aa,bb,cc。

我们通过一个例子来解释 $` 和 $‘:

var result = "2+3=5".replace(/=/, "$`----$‘");
console.log(result); //2+32+3----55

整个匹配下来只有一个 = ,所以$`表示 = 左边的内容,也就是2+3,对应的 $‘表示 = 右边的内容,也就是5。别忘了replace是将匹配到的内容替换掉,所以 = 被替换成了 2+3----5。

说到这肯定有同学问了,要死我有多个 = 符号你咋替换,别忘了replace是会遍历的,它会循环起来一步步替换,看个例子:

var result = "2=3=5".replace(/=/g, "$`$‘");
console.log(result); //223=532=355

由于加了修饰符g,所以这里会匹配量词,先说第一次,=左边是2,右边是3=5,组合起来就是 23=5,替换到=上去之后就是223=53=5。

匹配到了第二个 = ,注意,此时 = 左右不是以修改的字符作为标准,而是继续以修改前的原字符作为切割标准,所以第二个 = 左边是2=3,右边是5,组合起来就是2=35,替换到 223=53=5 第二个 = 上,于是就变成了 223=532=355。

不难理解,但是懒得理解....实际开发中使用$1此类字符串较多,其它字符就随缘了。但其实说到这里,我们还是强调一点,replace会遍历替换。

replace方法第二个参数还可以是一个回调函数,这里就可以验证我们说的replace会遍历的说法:

"1234 2345 3456".replace(/(\d)\d{2}(\d)/g, function (match, $1, $2, index, input) {
    console.log([match, $1, $2, index, input]);
});
// ["1234", "1", "4", 0, "1234 2345 3456"]
// ["2345", "2", "5", 5, "1234 2345 3456"]
// ["3456", "3", "6", 10, "1234 2345 3456"]

8.正则构造函数

与一般对象创建可以使用对象字面量,构造函数创建对象一样,正则也能使用构造函数创建。请记住一点,能使用字面量的情况一定使用字面量。有一种特殊情况必须使用构造函数创建,那就是正则内容是一个变量,比如:

function regexp(param) {
    var regex = new RegExp(param, ‘g‘);
    console.log(regex.test(‘听风是风‘));//true
    console.log(regex.test(‘时间跳跃 听风是风‘));//true
    console.log(regex.test(‘时间跳跃‘));//false
};
regexp(‘听风是风‘);

当然就算不是变量情况,我们也能用构造函数创建固定字符的正则,但原本有\的字符前你得多加一个\,所以你会发现这样的正则特别难读,像这样:

var regex = new RegExp(‘\\d+\\.\\d+‘, ‘g‘);
console.log(regex.test(‘3.14‘)); //true
console.log(regex.test(‘.3.14‘)); //false

//等同于
var regex = /\d+\.\d+/;

这个正则其实只是用来匹配浮点数,但\d和为了匹配小数点使用了转义符的\d前面都有\,所以统统再得加一个\。所以说嘛,能不用构造函数记得一定不要用。

9.正则source属性

使用构造函数创建正则的另一个问题就是,你不知道这是不是你想要的正则,对于这一点,我们可以通过source属性查看:

var regex = new RegExp(‘\\d+\\.\\d+‘, ‘g‘);
console.log(regex.source) //\d+\.\d+

10.构造函数属性

在前面聊$1,$&时,我想大家一定有个问题,难道我每次都要根据替换结果来反推这些字符的意思吗?有没有什么办法直接查看呢,其实是有的:

静态属性 描述 简写方式
 RegExp.input  最近一次目标字符串  RegExp["$_"]
 RegExp.lastMatch  最近一次匹配的文本  RegExp["$&"]
 RegExp.lastParen  最近一次捕获的文本  RegExp["$+"]
 RegExp.leftContext  目标字符串中lastMatch之前的文本  RegExp["$`"]
 RegExp.rightContext  目标字符串中lastMatch之后的文本  RegExp["$‘"]

通过静态属性或简写方式,我们可以直接查看这些字符到底匹配到了什么东西:

var regex = /\w(=)/g;
var string = ‘a=b=c‘;
string.match(regex);
console.log(RegExp.input);
console.log(RegExp["$_"]);
//a=b=c
console.log(RegExp.lastMatch);
console.log(RegExp["$&"]);
// b=
console.log(RegExp.lastParen);
console.log(RegExp["$+"]);
// =
console.log(RegExp.leftContext);
console.log(RegExp["$`"]);
// a=
console.log(RegExp.rightContext);
console.log(RegExp["$‘"]);
// c

 肆 ? 总

我读完了这本正则表示迷你书,很开心。然后现在断网了,博客无法提交保存,只能用USB链接电脑苟延残喘写个结尾提交了。

不管怎么说,我一路学过来也顺利学完,还是非常推荐大学花点时间好好学习正则,因为学会了之后在工作中每解决一个正则问题,那种前所未有的快感真是爽到爆炸!!!

好啦,半个月的学习顺利结束,也希望你能爱上正则,一起努力吧,有缘看到此文但陌生的你。

原文地址:https://www.cnblogs.com/echolun/p/12106730.html

时间: 2024-10-09 22:56:33

从零开始学正则(七:终章)的相关文章

从零开始学正则(二)

 壹 ? 引 我在从零开始学正则(一)这篇文章中介绍了正则横向模糊与纵向模糊匹配模式,以及常用的字符组与量词,掌握了这些其实已经算正则入门了.在文尾留下了两个正则问题,请写出匹配24小时制时间与16进制颜色的正则,在学习第二章之前我们先搞定这两个问题. 24小时制时间格式一般是09:30这样,小时的第一位数字可能是[0-2]三种情况之一,当为0,1时,第二位数字可以是[0-9]任意数字,当为2时第二位数字只能是0-3之间的数字.第三位数字只能是0-5之间的数字,最后一位数字只能是0-9之间. 我

从零开始学正则(四)

壹 ? 引 我在从零开始学正则(三)这篇博客中介绍了分组引用与反向引用的概念,灵活利用分组能让我们的正则表达式更为简洁.在文章结尾我们留下了两个问题,一问使用正则模拟实现 trim方法:二问将my name is echo每个单词首字母转为大写. 我们先来分析第一个问题,trim属于String方法,能去除字符串头尾空格,所以我们只需要写一个正则匹配头尾空格将其替换成空即可.空格属于空白符,所以这里需要使用字符组  \s ,空格可能有多个,所以要使用量词 + :其次我们需要匹配紧接开头后与结尾前

从零开始学正则(一)

 壹 ? 我为什么学正则 正则表达式是从侧面衡量一个程序员水平的标准,可以很肯定的说没有哪位高级开发不懂正则.在前端开发中使用正则表达式最频繁的场景莫过于表单验证,判断邮箱,判断手机号格式等等,那么我是怎么解决这些问题的呢,打开百度,输入“正则验证手机”回车,复制粘贴即可.我想大家应该看过不少关于常用正则整理的文章,我不理解正则,反正从来也记下来了. 古人云,熟读唐诗三百首,不会做诗也会吟.我会花三周左右系统化学习正则表达式,若你有兴趣可以与我一同学习(一起受苦),我相信学成之后即使无法立刻写出

从零开始学WCF(七)Message类

Message类是WCF的基本类 客户端与服务之间的所有通信最终都会产生要进行发送和接收的Message实例 通常不会与Message类直接进行交互.相反,您需要使用WCF服务模型构造(如数据协定,消息协定和操作协定)来描述传入消息和传出消息. 在以下情况下可能需要使用Message类 需要一种替代方式来创建传出的消息内容(例如,从磁盘上的文件直接创建消息),而不是序列化.net framework对象. 需要一种替代方式来使传入的消息内容(例如,需要将XSLT转换应用于原始XML内容),而不是

【MySQL函数】MySQL 5.5从零开始学第六章

说明:本文总结自:<MySQL 5.5从零开始学>第六章 MySQL中的函数包括: 数学函数.字符串函数.日期和时间函数.条件判断函数.系统信息函数和加密函数等. 函数: 表示对输入参数值返回一个具有特定关系的值. 一.数学函数 主要的数学函数有:绝对值函数.三角函数(包括正弦函数.余弦函数.正切函数.余切函数等). 对数函数.随机函数等.(PS:在有错误产生时,数学函数将会返回空值NULL) 1.1 绝对值函数ABS(x) mysql> SELECT ABS(1),ABS(-1.1),

【高德地图API】从零开始学高德JS API(七)——定位方式大揭秘

摘要:关于定位,分为GPS定位和网络定位2种.GPS定位,精度较高,可达到10米,但室内不可用,且超级费电.网络定位,分为wifi定位和基站定位,都是通过获取wifi或者基站信息,然后查询对应的wifi或者基站位置数据库,得到的定位地点.定位数据库可以不断完善不断补充,所以,越定位越准确.本文详细描述了,如果使用高德JS API来实现位置定位.城市定位的方法,包含了IP定位,浏览器定位,检索定位等多种网络定位方法.当然,如果您的手机有GPS功能,那么使用浏览器定位的时候,会自动获取GPS信息,使

从零开始学ios开发(七):Delegate,Action Sheet, Alert

Action Sheet和Alert是2种特殊的控件(暂且称之为控件吧,其实不是控件真正的控件,而是ios中的2个类,这2个类定义了2种不同类型的用于和用户交互的弹出框),Action Sheet是从底部弹出,上面有2个或者2个以上的选项供用户选择,Alert就是一个警告框,上面有1个或者1个以上的按钮供用户进行选择. 在继续这一篇的内容之前,稍微花点时间说一下ios中用到的Delegate Pattern(委托\代理模式). ios中有很多已经定义好的类可以供我们在编写程序时直接使用,例如UI

第23章、OnFocuChangeListener焦点事件(从零开始学Android)

在Android App应用中,OnFocuChangeListener焦点事件是必不可少的,我们在上一章的基础上来学习一下如何实现. 基本知识点:OnFocuChangeListener事件 一.界面 打开“res/layout/activity_main.xml”文件. 1.分别从工具栏向activity拖出2个编辑框EditText.控件来自Form Widgets. 2.打开activity_main.xml文件. [html] view plaincopy <LinearLayout

第86章、系统服务之TELEPHONY_SERVICE(从零开始学Android)

TelephonyManager类主要提供了一系列用于访问与手机通讯相关的状态和信息的get方法.其中包括手机SIM的状态和信息.电信网络的状态及手机用户的信息.在应用程序中可以使用这些get方法获取相关数据. TelephonyManager类的对象可以通过Context.getSystemService(Context.TELEPHONY_SERVICE)方法来获得,需要注意的是有些通讯信息的获取对应用程序的权限有一定的限制,在开发的时候需要为其添加相应的权限. 一.设计界面 1.布局文件