• 从零开始学正则(五),了解正则解构,让复杂正则化繁为简


     壹 ❀ 引

    我在  从零开始学正则(四) 一文中讲述了正则匹配的回溯法,以正则匹配过程引出了正则书写也会存在性能问题,并阐述了贪婪匹配,惰性匹配以及分支匹配时与回溯的中中关系。当然,对于初学者而言除了能写出正则以外,能读懂任意一段正则也是非常重要的。那么本篇文章主要针对正则表达式拆分展开分析,相信大家在阅读之后再面对各种变态长度的正则时,都能有理可据,化繁为简的拆分理解。

    说在前面,正则学习系列文章均为我阅读 老姚《JavaScript正则迷你书》的读书笔记,文中所有正则图解均使用regulex制作。那么本文开始!

     贰 ❀ 正则的解构与操作符

    编程语言一般都有操作符(百科),但只要说到操作符就不得不讨论操作符的优先级,因为一堆操作符在一起,系统自己也得知道谁该先执行,谁要后执行。

    那么正则中的操作符是什么呢?正则中的操作符体现在正则结构中,而结构又由特殊字符与普通字符构成。

    JavaScript中的正则结构大致有这些:字符字面量、字符组、量词、锚、分组、分支、反向引用。也就是前几章节讲过的知识点,我们简单复习一遍:

    字符字面量:当我们具体匹配某个字符时所写的正则字段,比如a匹配字段“a”,123匹配字段“123”,. 匹配小数点等。

    字符组:当某个位置的字符可能是多种情况之一时,比如匹配任意一个数字,可以使用字符组[0-9],可简写为d。除此之外还有反义字符组,比如匹配除了数字之外的任意字符,可以用[^0-9],可简写为D。

    量词:当某个字符需要出现多次时可使用量词加以修饰,比如数字可能出现1次或更多次,可以写成d{1,},简写便是d+。

    锚:如果说字符字面量以及字符组是匹配的基础,那么锚的功能也是如此,只是前者用于匹配字符,而锚用于匹配位置。比如我们要匹配数字前面的位置可以写(?=d)。

    分组:分组表示一个整体,使用圆括号()表示,比如(ab)+表示字母ab至少会出现一次。

    分支:分支使用管道符 | 实现,比如分支 12|34,正则会先尝试使用12进行匹配,如果行不通就会切换成分支34进行匹配,注意分支是惰性匹配。

    其中涉及到的操作符以及优先级如下:

    操作符描述 操作符 优先级
    转义符 1
    圆括号和方括号 (p)、(?:p)、(?=p)、(?!p)、[p] 2
    量词 {m}、{m,n}、{m,}、?、+、* 3
    位置和序列 ^、$、元字符、一般字符 4
    管道符 | 5

    知道了这些,我们来从优先级的角度解析正则表达式 /ab?(c|de*)+|fg/ 

    由于不存在转义符,我们通过优先级第二高的括号来拆分正则,首先分组 (c|de*) 是一个整体,而在分组中字母 e 后紧跟了一个量词 *,因此 e* 是一个整体,表示e会出现任意次。最后括号中使用了优先级最低的管道符,所以正则在运行时,一定是先看字母 c ,再看 de*,这里形成了分支。

    分析完分组中的内容,再看其它部分,可以得到a、b?、(c|de*)+、f、g这些组合结构,而 ab?(c|de*)+ 和 fg 又使用管道符分割,所以这是两个比较大的分组。当然就算不这么拆分,通过前面的学习解析起来也不是什么难事。我们结合图解来确认下:

     叁 ❀ 结构与操作符可能犯错的点

    1.分支不使用分组包裹

    在匹配整个字符串时,我们通常会添加脱字符^与美元符$,如果我们要匹配字符 abc 或 def 时,容易写成 /^abc|def$/,而此时正则的意思是匹配分支^abc或者def$,它的图解是这样:

    而正确的写法应该是利用分组将分支结构进行包裹,所以应该是这样 ,图解为:

    2.量词接量词

    这个是我前面常犯得错误....量词与量词不能紧接着写。比如我们希望匹配数字1,2,3其一,,且这段数字长度为3的倍数,正则很容易写成 /^[123]{3}+$/ ,然后你会收到浏览器红色问候:

    var regex = /^[123]{3}+$/;
    regex.test(123123);

    而正确的写法是 [abc]{3} 是一个整体,所以需要使用分组的括号包裹,完整就是这样:

    var regex = /^([123]{3})+$/;
    regex.test(123123);// true

    3.元字符转义问题

    正则中的元字符包括 ^、$、.、*、+、?、|、、/、(、)、[、]、{、}、=、!、:、- 等。在我们需要匹配这些字符时,到底需不需要转义呢?当然全转义肯定也不会有问题:

    var string = "^$.*+?|\/[]{}=!:-,";
    var regex = /^$.*+?|\/[]{}=!:-\,/;
    regex.test(string); //true

    开发中是否转义还是根据实际情况决定,比如我们真的要匹配字符 “[123]” 时 ,可以在 [ 前添加转义符,表示这不是一个反义字符组。

    var string = "[123]";
    var regex = /^[123]$/;
    regex.test(string); //true

    当然上述正则其实可以简写成 /^[123]$/ ,因为 [ 前已被转义,后面的 ] 无法成对,所以可以省略转义。

    假设我们要匹配 “[^123]” 时可以写成这样,注意 ^ 的转义不可少:

    var string = "[^123]";
    var regex = /^[^123]$/;
    regex.test(string); //true

    同理,假设要匹配 “{1,3}”,正则我们可以写成 /{3,5}/

    注意有个特例,当匹配圆括号时,前后转义符都得加,因为只写一个会报错。看个例子:

    var string = "(123)";
    var regex = /^(123)$/;
    regex.test(string); //true

     肆 ❀ 总

    这一章节内容的内容还算简单,了解正则的操作符划分对于复杂的正则拆分非常有帮助。当然在正则熟练后,我们甚至能一眼看出正则所传递的匹配含义。我们通过思维导图简单做个整理:

    那么本文就写到这里,我要学习第六章节了。

  • 相关阅读:
    3089:爬楼梯
    7592:求最大公约数问题
    JVM中内存回收深入分析,各种垃圾收集器
    PKU 1064 Cable master
    【面试&笔试】ASP.NET的相关问题
    1027. Colors in Mars (20) PAT
    DB_WRITER_PROCESSES与LOG_ARCHIVE_MAX_PROCESSES
    fedora下体验gentoo安装
    一个整数数组里面,除了两个数之外,其他的数字都出现了两次,写一个程序找出这两个数
    [置顶] export命令-linux
  • 原文地址:https://www.cnblogs.com/echolun/p/12084046.html
Copyright © 2020-2023  润新知