博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript 正则入门到掌握
阅读量:6166 次
发布时间:2019-06-21

本文共 6773 字,大约阅读时间需要 22 分钟。

最近学习了 AST 抽象语法树,在代码解析的过程中广泛使用了正则表达式,由此认识到自己在正则基础方面的薄弱,虽然清楚每个符号所表示的含义,但是当一大串正则符号出现在自己面前时,还是会懵逼一会,无法融汇贯通的掌握正则。

下面是自己整理的正则基础知识和收集到的一些实战训练。希望通过训练和笔记来加深自己对正则表达式的掌握!

基础

正则表达式(Regular Expression,在代码中常简写为regex、regexp或RE)使用单个字符串来描述、匹配一系列符合某个句法规则的字符串搜索模式。

搜索模式可用于文本搜索和文本替换。

下面是正则的基础语法,只要熟悉就行,重要还是结合实际多运用正则,才能真正的掌握正则。

创建正则表达式

方式1:

// 语法格式为: /正则表达式主体/修饰符(可选)// 案例var patt = /var/i// var 是正则表达式主体// i 是修饰符复制代码

方式2:

// 创建 RegExp 对象// new RegExp(正则表达式主体[, 修饰符])// RegExp(正则表达式主体[, 修饰符])var patt = new RegExp('var', 'i')复制代码

注意: /var/i 创键的正则表达式对象和 new RegExp(var, i) 创建的对象并不相等。虽然它们匹配规则一样。

指定匹配模式的修饰符有:

修饰符 描述
i 执行对大小写不敏感的匹配。
g 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m 执行多行匹配。
s 允许 . 去匹配多行
u Unicode; 将模式视为Unicode序列点的序列Unicode;
y 粘性匹配; 仅匹配目标字符串中此正则表达式的lastIndex属性指示的索引(并且不尝试从任何后续的索引匹配)。

字符类别(Character Classes)

元字符是正则表达式规定的一个特殊代码

代码 说明
. 匹配除换行符以外的任意字符。注意:/[.]/中匹配的是符号点.
\w 匹配字母或数字或下划线或汉字 [A-Za-z0-9_]
\s 匹配任意的空白符 [\f\n\r\t\v​\u00a0]
\d 匹配数字 [0-9]
\ 用于转义

边界(Boundaries)

代码 说明
\b 匹配单词的开始或结束 例如:/\bno/ 匹配 "at noon" 中的 "no"
^ 匹配字符串的开始
$ 匹配字符串的结束

分组(Grouping)与反向引用(back references)

字符 含义
(x) 捕获括号。匹配 x 并且捕获匹配项。 /(foo)/ 匹配且捕获 "foo bar." 中的 "foo"。被匹配的子字符串可以通过 元素[n] 中找到,或 RegExp 对象的属性 $n 中找到。会消耗性能,如果需要捕获的数组,可以使用非捕获括号
\n 反向引用(back reference),指向正则表达式中第 n 个括号(从左开始数)中匹配的子字符串。/apple(,)\sorange\1/ 匹配 "apple, orange, cherry, peach." 中的 "apple,orange,"(最后的,号)
(?:x) 非捕获括号(non-capturing parentheses)。匹配 x 不会捕获匹配项。匹配项不能够从结果再次访问。(推荐)

重复

代码/语法 说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
x*?
x+?
最小可能匹配,非贪婪模式。/".*?"/ 匹配 "foo" "bar" 中的 "foo"
{n} 重复n次
{n,m} 重复n到m次

断言(Assertions)

代码/语法 说明
x(?=y) 只有当 x 后面紧跟着 y 时,才匹配 x。/Jack(?=Sprat)/ 只有在 'Jack' 后面紧跟着 'Sprat' 时才匹配
x(?!y) 只有当 x 后面不是紧跟着 y 时,才匹配 x。

实例属性

  • RegExp.prototype.global 是否开启全局匹配
  • RegExp.prototype.ignoreCase 是否要忽略字符的大小写。
  • RegExp.prototype.lastIndex 下次匹配开始的字符串索引位置。
  • RegExp.prototype.multiline 是否开启多行模式匹配(影响 ^ 和 $ 的行为)。
  • RegExp.prototype.source 正则对象的源模式文本。
  • RegExp.prototype.sticky 是否开启粘滞匹配。

实例方法

  • RegExp.prototype.exec() 在目标字符串中执行一次正则匹配操作。
  • RegExp.prototype.test() 测试当前正则是否能匹配目标字符串。
  • String.prototype.match(pattern) 根据pattern对字符串进行正则匹配,返回匹配结果数组,如匹配不到返回null
  • String.prototype.replace(pattern, replacement) 根据pattern进行正则匹配,把匹配结果替换为replacement, 返回一个新的字符串

实战

改变文本结构

var re = /(\w+)\s(\w+)/// \w 匹配 [A-Za-z0-9_]// + 重复一次或更多次// \s 匹配 空格符// () 捕获括号var str = "John Smith"var newstr = str.replace(re, "$2, $1")// $1: 捕获的值为 John // $2: 捕获的值为 Smithconsole.log(newstr) //  转换后为 "Smith, John"复制代码

训练1: 请通过正则表达式,把 () => 1+1 修改为 function () { return 1+1 }

多行匹配

var s = "Please yes\nmake my day!";s.match(/yes.*day/);// . 符号默认不匹配 \n// 可以改为 /yes.*day/s ,使用 s 标识符来匹配多行// 返回 nulls.match(/yes[^]*day/);// ^ 匹配字符串的开始// [^]* 表示匹配任何字符,包括换行符// 返回 yes\nmake my day复制代码

训练2: 请通过正则表达式,获取到 ab a\nb ab 中的 a\nb 字符串

使用带有 ”sticky“ 标志的正则表达式

var text = "First line\nsecond line";var regex = /(\S+) line\n?/y;// () 捕获括号// \S+ 匹配一次或更多次非空白符// \n? 匹配零到一次换行符var match = regex.exec(text);console.log(match[1]);  // 匹配到 First// 输入 match[0] 返回的是 First line\n 所要进行匹配的文本console.log(regex.lastIndex); // 匹配到位置 11var match2 = regex.exec(text);console.log(match2[1]); // 匹配到 Secondconsole.log(regex.lastIndex); // 匹配到位置 22var match3 = regex.exec(text);console.log(match3 === null); // 返回 true, 没有匹配到结果复制代码

训练3: 有文本"price: 10¥\nprice: 20¥\nprice: 30¥", 请获取到 price 的值,保存到数组中,结果输出为 ['10', '20', '30']

匹配链接

提取链接传递参数的值

// 提取 str 的 name 和 agevar str = 'https://www.baidu.com?name=jawil&age=23'// 获取name的值var regName = /\?name=([^&]*)/// \? 表示匹配 ? 字符,从该字符开始匹配// name= 表示匹配 name= 的字符串// [^&]* 表示匹配字符直到遇到 & 符号为止// ([^&]*) 表示捕获匹配到的结果// match 的值为 ["?name=jawil", "jawil"]var match = regName.exec(str)console.log(match[1]) // 返回 jawil复制代码

训练4: 请修改正则表达式,获得到 age 的属性值, 该正则表达式是怎么样的?

数字格式化

把 1234567890 格式化为 1,234,567,890

var str = '1234567890'var reg = /\B(?=(\d{3})+(?!\d))/g// \b 匹配单词的开始或结束// \B 匹配单词的非开始和非结束的位置//     \Bon 匹配 ' on' 为 null,即开头不能为 on//     \Bon 匹配 ' onon' 为 on,匹配后一个 on// (\d{3}+) 捕获1个或多个的3个连续数字// (?!\d) 断言: 表示3个数字不允许后面跟着数字// (\d{3}+)+(?!\d) 表示匹配的后边界跟着 3*n(n>=1)的数字.// (?=) 断言: 表示后边必须跟着的内容//     \B(?=(\d{3}+)+(?!\d)) 匹配非边界后跟着的3个数字// 运行过程// 第一次匹配到为 ["", "890"] 没有匹配到文本,但捕获到3个数字// 替换后为 1234567,890// 第二次匹配到为 ["", "567"]// 替换后为 1234,567,890// 第三次匹配到为 ["", "234"]// 替换后为 1,234,567,890// 最后一次运行结果为 null 替换结束var format = str.replace(reg, ',')console.log(format) // 1,234,567,890复制代码

训练5: 通过正则表达式把字符串'255255255255' 格式化为 ip地址格式 '255.255.255.255'

训练6: 编写正则表达式测试传入的ip地址是否正确,例如传入 1.1.1.1 返回 true, 传入 1.1.1.257 返回 false

零宽断言

如果不想匹配符号,只匹配一个位置,就要用到"零宽断言"。用于帮我们匹配文本,而它自身不会被匹配到内容中去。

var str = 'ttabttdef'var reg = /tt(?=ab)/reg.exec(str)// 匹配到的是 tt, 只有后面跟的是 ab 的才匹配var reg1 = /tt(?=ab)ab/reg1.exec(str)// 匹配到的是 ttab, 断言只匹配位置, 所以断言匹配的这些字符会接下去进行匹配。// 而不会因为被断言匹配过了就跳过这些字符。复制代码

训练7: 使用断言匹配出字符串 <div>red blue green</div> 中的字符串 red blue green

训练8: 使用断言,匹配出<xxx>任意字符串</xxx>中的 任意字符串, 其中 xxx 表示标签名, 比如匹配出<div>adsasdfa<br>asdfsd</div>中的 adsasdfa<br>asdfsd

虚拟 DOM 标签解析

在Vue源码中构建虚拟DOM, 首先把 template 编译成AST语法树, 再转换为 render 函数 最终返回一个VNode(VNode就是Vue的虚拟DOM节点)

而要编译 template, 首先就是要对 template 进行解析。解析的过程中使用到了正则表达式, 比如解析下面代码

var dom = `
{
{item}}
`// 匹配变量名const variable = /[a-zA-Z_][\w\-\.]*/// 匹配标签名const tagName = /^<([a-zA-Z_][\w\-\.]*)/console.log(tagName.exec(dom)[1])// 返回 div// 匹配属性const attr = /\s*([^\s'"<>/=]+)(?:=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+))/g// \s*([^\s'"<>/=]+) 匹配属性名// (?:=) 匹配赋值号 = // (?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)) 匹配属性值,包括 ""、 '' 和其它形式var res = attr.exec(dom)console.log(res[1]) // 返回 :classconsole.log(res[2]) // 返回 "c"// 匹配 {
{}} 里的内容const val = /{
{(.*)}}/ console.log(val.exec(dom))// 返回 item复制代码

答案

训练1:

var str = '() => 1+1'var reg = /(\(\))\s(=>)\s([^]*)/var str1 = str.replace(reg, 'function $1 { return $3 }')复制代码

训练2:

var s = "ab a\nb ab"s.match(/a\nb/)复制代码

训练3:

var str = 'price: 10¥\nprice: 20¥\nprice: 30¥'var reg = /\D*(\d+)/yvar a = []while((match = reg.exec(str)) !== null) {  a.push(match[1])}console.log(a)复制代码

训练4:

var str = 'https://www.baidu.com?name=jawil&age=23'var regAge = /\&age=([^&]*)/var match = regAge.exec(str)复制代码

训练5:

var pattern = /\B(?=(\d{3})+(?!\d))/gvar str = '255255255255'str.replace(pattern, '.')复制代码

训练6:

  • 首先要匹配的是 0 ~ 255
  • 匹配 50 ~ 55: 5[0-5]{1}
  • 匹配 0 ~ 49: [0-4]\d{1}
  • 匹配 200 ~ 255: 2(5[0-5]{1}|[0-4]\d{1})
  • 匹配 0 ~ 199: [0-1]?\d{1,2}
  • 完整匹配 0 ~ 255:
    • (2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})

组合成检测的ip 的正则表达式

var reg1 = /(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})/// 把后边3段检测代码进行简化后var reg2 = /(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})(\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})){3}/复制代码

训练7:

var str="
red blue green
"var reg = /
(.*)(?=<)/reg.exec(str)[1] // red blue green复制代码

训练8:

var str = '
asdas
dfadfsdasdf
'var reg = /^<(\w+)>(.*)(?=<\/\1>)/reg3.exec(str)[2] // asdas
dfadfsdasdf复制代码

参考资料

转载地址:http://ymuba.baihongyu.com/

你可能感兴趣的文章
centos 文档的压缩和打包 gzip,bzip2,xz,zip,unzip,tar,tgz 第九节课
查看>>
Async and Await 异步和等待
查看>>
【转】六年软件测试感悟-从博彦到VMware
查看>>
wcf 配置与代码创建
查看>>
java代码 分解EXCEL(一)
查看>>
Swift - 异步加载各网站的favicon图标,并在单元格中显示
查看>>
#define XXX do{ XXX } while(0) 为什么使用
查看>>
.NET中的GDI+
查看>>
Axure RP7.0 使用记录手册
查看>>
python之函数用法divmod
查看>>
使用 xtrabackup 进行MySQL数据库物理备份
查看>>
[安卓] 16、ListView和GridView结合显示单元实现自定义列表显示效果
查看>>
app服务器
查看>>
css+div布局案例
查看>>
HTTP缓存是如何实现
查看>>
【转】 Ubuntu samba服务器搭建及测试--不错
查看>>
如何编写一个程序猿另一个面试官眼前一亮的简历
查看>>
Swift - 11 - nil聚合运算
查看>>
CSS样式表——布局练习(制作360网页)
查看>>
Windows下搭建Android NDK开发环境及命令行编译
查看>>