javascript正则表达式入门先了解这些
前言
此内容由学习《JavaScript正则表达式迷你书(1.1版)》整理而来(于2020年3月30日看完)。此外还参考了MDN上关于Regex和String的相关内容,还有ECMAScript 6中关于正则的扩展内容,但不多。在文章末尾,会放上所有的链接。
迷你书共七章,我都做了相应的标号。不过我将【7】7种方法放在了前面,讨论了具体情境下怎么正确使用函数的问题(其实是我自己一直被这个问题困扰,书上的例子为什么用这个方法,为什么这个方法这里返回这样,那里却不是这样,把我搞崩溃了),也建议大家先搞懂这个吧。
本文重点
正则的 2 种创建
正则的 6 个修饰符(i、g、m、u、y、s)
【7】.用到正则的 7 种方法(RegExp(exec、test),String(search、match、matchAll、replace、split))
正则表达式中的6种结构(字符字面量、字符组、量词、锚、分组、分支)
【1】.字符匹配:字面量、字符组、量词。重点还有贪婪匹配与惰性匹配
【2】.位置匹配:^ 、$ 、 \b 、\B 、(?=abc) 、 (?!abc) 的使用
【3】.括号的作用:分组和分支结构。知识点有:分组引用($1的使用)、反向引用(\1的使用)、括号嵌套、括号结合量词
【4】.回溯原理 (本文大多介绍的是实践中用的基础知识,后期对此可能会单独写篇,先占个坑)
【5】.正则的拆分:重点是操作符的优先级
【6】.正则表达式的构建(本章写了些提高正则准确性和效率的内容,也先占个坑)
简单实用的正则测试器
2种创建
语法:/pattern/flags
var regexp = /\w+/g;
var regexp = new RegExp('\\w+','g');
6个修饰符
重点介绍全局与非全局:
全局就是说在字符串中查找所有与正则式匹配的内容,因此会有>=1个结果。
而为了得到这个所谓的所有匹配内容的过程,后面介绍的函数还有一次执行和多次执行之别。
如果正则表达式中存在分组,该模式下各个函数返回的结果也不一样。
非全局就简单了,它表示匹配到了一个就不会再继续往后匹配了,因此都是一次就得结果。
7种方法
以下图在我学习的过程中,修改了很多遍,才得出了这个最简洁明了的版本。
【补充1】全局模式对 exec 和 test 的影响
正则实例有个 lastIndex 属性,表示尝试匹配时,从字符串的 lastIndex 位开始去匹配。
全局匹配下,字符串的四个方法,每次匹配时,都是从 0 开始的,即 lastIndex 属性始终不变。
而正则实例的两个方法 exec、test,当正则是全局匹配时,每一次匹配完成后,都会修改 lastIndex。(详见下面示例)
如果是非全局匹配,自然都是从字符串第 0 个字符处开始尝试匹配:
exec()
在全局状态下,需要一次次执行直到末尾才能得到所有匹配项,这里只是手动模拟下。当然最好是用循环实现,while ((match = regexp.exec(str)) !== null) {//输出}
var regexp = /a/g;
console.log( regexp.exec("a"), regexp.lastIndex );// [ 'a', index: 0, input: 'a', groups: undefined ] 1
console.log( regexp.exec("aba"), regexp.lastIndex );// [ 'a', index: 2, input: 'aba', groups: undefined ] 3
console.log( regexp.exec("ababc"), regexp.lastIndex );// null 0
注意:该部分将的所有正则表达式不知道的都先不要急,慢慢来。
【补充2】全局下match、exec、matchAll示例
match()和exec()示例:
//全局模式下,匹配所有能匹配到的
var regexp1 = /t(e)(st(\d?))/g;
var str1 = 'test1test2';
console.log(str1.match(regexp1)); //match返回所有匹配项组成的数组[ 'test1', 'test2' ]
console.log(regexp1.exec(str1))// exec第一次执行返回第一个匹配项和它的分组['test1', 'e','st1','1',index:0,input:'test1test2',groups:undefined]
console.log(regexp1.exec(str1))//exec第二次执行返回第二个匹配项和它的分组['test2', 'e','st2','2',index:5,input:'test1test2',groups:undefined]
console.log(regexp1.exec(str1))//exec第二次执行已经到末尾了,因此第三次结果为null
//非全局模式下,只匹配到第一项就停止
var regexp2 = /t(e)(st(\d?))/;
var str2 = 'test1test2';
console.log(str2.match(regexp2)); //match['test1', 'e','st1','1',index:0,input:'test1test2',groups:undefined]
console.log(regexp2.exec(str2)) //exec['test1', 'e','st1','1',index:0,input:'test1test2',groups:undefined]
matchAll()示例:
matchAll()
是es6的用法,记住它返回的就是一个迭代器。可以用for...of
循环取出,也可以用...迭代器
运算符或者Array.from(迭代器)
将它转为数组。
var array1 = [...str1.matchAll(regexp1)];
console.log(array1)
//['test1','e','st1','1',index: 0,input: 'test1test2',groups: undefined]
//['test2','e','st2','2',index: 5,input: 'test1test2',groups: undefined]
var array2 = [...str2.matchAll(regexp2)];
console.log(array2)
//['test1','e','st1','1',index: 0,input: 'test1test2',groups: undefined]
到目前为止,我们应该积攒了很多问号了。我学的过程中有以下两个问题:
1./t(e)(st(\d?))/g
和/t(e)(st(\d?))/
的区别我知道了,但t(e)(st(\d?))
这是什么意思呢?
2.上文所谓的“与正则表达式匹配的内容”和“匹配项中分组捕获的内容”怎么理解?
那就带着问题看后面的内容吧。
7种结构 -字符匹配
字面量
字符组
需要强调的是,虽叫字符组(字符类),但只是其中一个字符。
如果字符组里的字符特别多,可用连字符 -
来省略和简写(见表格示例)。
示例:全局匹配,使用match()
方法返回字符串中与表达式匹配的所有内容。[123]
表示这个位置的字符可以是1
、2
、3
中的任意一个
量词
有了一个字符,那我我们就会考虑到需要它出现几次,那么量词来了。
示例:全局匹配,使用match()
方法返回字符串中与表达式匹配的所有内容。b{2,5}
表示字符b
出现2到5次。
贪婪匹配与惰性匹配
贪婪匹配/\d{2,5}/
表示数字连续出现 2 到 5 次,会尽可能多的匹配。你如果有 6 个连续的数字,那我就要我的上限 5 个;你如果只有 3 个连续数字,那我就要3个。想要我只取 2 个,除非你只有两个。
惰性匹配/\d{2,5}?/
表示虽然 2 到 5 次都行,当 2 个就够的时候,我也不贪,我就取两个。
对惰性匹配的记忆方式是:量词后面加个问号,问一问你知足了吗,你很贪婪吗?注意是 量词后面 量词后面 量词后面,重要的事说三遍。还是来个例子吧,?与??:
'testtest1test2'.match(/t(e)(st(\d?))/g)
的结果就是[ 'test', 'test1', 'test2' ]
'testtest1test2'.match(/t(e)(st(\d??))/g)
的结果就是[ 'test', 'test', 'test' ]
7种结构 -位置匹配
位置理解
位置特性
1.对于位置的理解,我们可以理解成空字符 ""。
因此,把 /^hello$/
写成 /^^hello$$$/
,是没有任何问题的。甚至 /(?=he)^^he(?=\w)llo$\b\b$/
;
2.整体匹配时,自然就需要使用 ^
和 $
7种结构 - 括号的作用
分组和分支结构
到目前为止,我们对于 match(/t(e)(st(\d?))/g)
应该完全理解了(包括 match()
的使用,全局修饰符g
,表示字符组的\d
和表示量词的?
,且也知道了这是贪婪匹配)。那么,里面的括号又表示什么意思呢?这就涉及到分组捕获了。
以日期为例,假设要匹配的格式是 yyyy-mm-dd
的:
对比这两个可视化图片,我们发现,与前者相比,后者多了分组编号,如 Group #1,这样正则就能在匹配表达式的同时,还能得到我们想要从匹配项中捕获的分组内容。即用 ()
括起来的内容。到此为止,t(e)(st(\d?))
涉及到的分组捕获知识点也就结束了。
再介绍下分支结构,由于操作符 |
的优先级最低,因此需要将其放于括号中:
分支结构形如(regex1|regex2)
,字面意思,即这里可以匹配 regex1
或者 regex2
之一,捕获分组的时候也是二者之一 ,例子:
引用分组
使用相应 API 来引用分组。
提取数据
替换replace
当第二个参数是字符串时,如下的字符有特殊的含义。其中,第二个是 $&
:
反向引用分组
除了使用相应 API 来引用分组,也可以在正则本身里引用分组。但只能引用之前出现的分组,即反向引用。
以前面的日期为例,假设我们想要求分割符前后一致怎么办?即杜绝 2016-06/12
这样中间分割符不一致的情况,此时需要使用反向引用:
注意里面的 \1,表示的引用之前的那个分组 (-|\/|\.)
。不管它匹配到什么(比如 -),\1 都匹配那个同样的具体某个字符。
我们知道了 \1 的含义后,那么 \2 和 \3 的概念也就理解了,即分别指代第二个和第三个分组。需要注意:
\10
表示第十个分组,如果真要匹配\1
和0
的话,使用(?:\1)0
或者\1(?:0)
。反向引用引用不存在的分组时候,匹配
反向引用的字符本身。例如\2
,就匹配"\2"
。
分组括号嵌套
针对 "1231231233".match(/^((\d)(\d(\d)))\1\2\3\4$/)
的可视化形式如下,一目了然:
分组括号结合量词
分组后面有量词的话,分组最终捕获到的数据是最后一次的匹配。分组引用与反向引用都是这样例子:
非捕获括号
如果只想要括号最原始的功能,但不会引用它,即,既不在 API 里引用,也不在正则里反向引用。
可以使用非捕获括号 (?:p)
和 (?:p1|p2|p3)
,如下代码,执行matchAll()
,虽然有括号,但不会得到捕获的分组内容。
正则的拆分
不仅要求自己能解决问题,还要看懂别人的解决方案。代码是这样,正则表达式也是这样。如何能正确地把一大串正则拆分成一块一块的,成为了破解“天书”的关键。
这里,我们来分析一个正则 /ab?(c|de*)+|fg/
:
思考下:
/^abc|bcd$/
和/^(abc|bcd)$/
。
/^[abc]{3}+$/
和/^([abc]{3})+$/
操作符优先级
操作符转义
所有操作符都需要转义
^、$、.、*、+、?、|、\、/、(、)、[、]、{、}、=、!、:、- ,
用\
符号进行转义
到这里,对正则表达式也算是入了个小门了,即对正则的一些基本操作也有了了解,也能看懂别人的正则表达式,就算是copy也能灵活的改动了。
简单实用的正则测试器
效果图:
源代码:git
相关链接
老姚 《JavaScript 正则表达式迷你书》下载
阮一峰《ECMAScript 6 入门 - 正则的扩展》
MDN RegExp.prototype.exec()
MDN RegExp.prototype.test()
MDN String.prototype.search()
MDN String.prototype.match()
MDN String.prototype.matchAll()
MDN String.prototype.replace()
MDN String.prototype.split()
javascript正则表达式入门先了解这些的更多相关文章
- 正则系列——JavaScript正则表达式入门心得
我发现有个别字符被这个编辑器给刷掉了,但是灰色区域显示正常,以灰色区域代码为准 什么玩意? 在我刚开始学习编程的时候,就听过正则了,也听说正则很牛逼,懂正则的更牛逼.但是苦于没有人指点,也没有使用正则 ...
- JavaScript 正则表达式入门教程
正则表达式是描述一组字符串特征的模式,用来匹配特定的字符串 主要分三个部分:基本语法.RegExp对象的方法.JS中支持正则表达式的String对象方法 一.基本语法 在JS中,正则表达式为对象,用如 ...
- javascript正则表达式入门
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...
- JavaScript正则表达式详解(一)正则表达式入门
JavaScript正则表达式是很多JavaScript开发人员比较头疼的事情,也很多人不愿意学习,只是必要的时候上网查一下就可以啦~本文中详细的把JavaScript正则表达式的用法进行了列表,希望 ...
- 正则表达式入门教程&&经典Javascript正则表达式(share)
前言 例子: ^.+@.+\\..+$ 这样的代码曾经多次把我自己给吓退过.可能很多人也是被这样的代码给吓跑的吧.继续阅读本文将让你也可以自由应用这样的代码. 正文 教程:正则表达式30分钟入门教程 ...
- 正则表达式基于JavaScript的入门详解
关于正则表达式,和很多前辈聊起这个知识点时,他们的反馈都比聊其他技术谦逊,而和很多刚入门的程序员讨论时甚至会有觉得你看不起他. 的确,正则表达式从通常的应用来看,的确不难,比如电话,邮箱等验证.语法, ...
- 正则表达式入门教程&&经典Javascript正则表达式----share
前言 例子: ^.+@.+\\..+$ 这样的代码曾经多次把我自己给吓退过.可能很多人也是被这样的代码给吓跑的吧.继续阅读本文将让你也可以自由应用这样的代码. 正文 教程:正则表达式30分钟入门教程 ...
- 正则表达式入门教程&&经典Javascript正则表达式
前言 例子: ^.+@.+\\..+$ 这样的代码曾经多次把我自己给吓退过.可能很多人也是被这样的代码给吓跑的吧.继续阅读本文将让你也可以自由应用这样的代码. 正文 教程:正则表达式30分钟入门教程 ...
- javascript 正则表达式 详细入门教程
1.什么是正则表达式 定义: 一个用来搜索.匹配.处理一些符合特定语法规则的一个强大的字符串处理工具. 用途: 进行特定字符和字符串的搜索 替换字符串中指定的字符或字符串 验证字符串是否符合需求 2. ...
随机推荐
- Oracle字段
pl/sql查看当前数据库编码语句:select userenv('language') from dual; 测试结果:数据库字符集编码是utf-8 1.nvarchar2(size) ,size不 ...
- SpringBoot(七)-SpringBoot JPA-Hibernate
步骤 1.在pom.xml添加mysql,spring-data-jpa依赖2.在application.properties文件中配置mysql连接配置文件3.在application.proper ...
- 安卓权威编程指南 挑战练习(第26章 在 Lollipop 设备上使用 JobService)
26.11 挑战练习:在 Lollipop 设备上使用 JobService 请创建另一个 PollService 实现版本.新的 PollService 应该继承 JobService 并使用 Jo ...
- Python——13定制类
*/ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...
- TCP传输连接管理
TCP传输连接管理 一.传输连接的三个阶段 1.1.概述 传输连接就有三个阶段,即:连接建立.数据传送和连接释放. 连接建立过程中要解决以下三个问题: 要使每一方能够确知对方的存在. 要允许双方协商一 ...
- PHP 深度理解preg_quote()函数
php手册上说,preg_quote()函数的作用是转义正则表达式字符.那么下面我们来深入了解下这个函数是怎么使用的: 说明:preg_quote()函数常和preg_replace()函数一起使用. ...
- python之嵌套 闭包 装饰器 global、nonlocal关键字
嵌套: 在函数的内部定义函数闭包: 符合开放封闭原则:在不修改源代码与调用方式的情况下为函数添加新功能 # global 将局部变量变成全局变量 num = 100 def fn1(): globa ...
- CyclicBarrier源码探究 (JDK 1.8)
CyclicBarrier也叫回环栅栏,能够实现让一组线程运行到栅栏处并阻塞,等到所有线程都到达栅栏时再一起执行的功能."回环"意味着CyclicBarrier可以多次重复使用,相 ...
- 2020ubuntu1804server编译安装redis5笔记(二)配置redis
前一篇笔记记录了ubuntu1804server编译安装redis5,接下来要配置redis5了 网址:https://www.cnblogs.com/qumogu/p/12435694.html 第 ...
- VOIP RTP RTSP 实现 Baresip 源码分析
RTP 使用 udp 进行数据传输,udp 是不能保证,数据包一定可以到达的,也不提供时序.同时还有 MTU 限制. RTCP 用来配合 RTP 提供,传输报告,会话建立和退出. 一大批参考规范 * ...