正则表达式 词法分析,正则表达式基本语法
正则表达式是字符串操作的逻辑公式,是处理文本数据的重要而复杂的技术。那么如何快速掌握正则表达式呢?下面这篇文章推荐一种学习方法:通过AST。希望对大家有帮助!
字符串处理基本都用正则表达式,字符串的匹配、提取、替换都非常方便。
但是学习正则表达式还是比较困难的,比如贪婪匹配、非贪婪匹配、捕获子群、非捕获子群等。这些不仅初学者难以理解,很多工作了几年的人也难以理解。
如何学习正则表达式?如何快速掌握正则表达式?
推荐一个学习规则的好方法:通过 AST 来学习。
正则表达式的匹配原理是把模式串 parse 成 AST,然后通过这个 AST 去匹配目标字符串。
模式中的各种信息将在解析后保存在AST中。AST是抽象语法树,意思是抽象语法树。顾名思义,它是按照语法结构组织起来的树。所以你可以从AST的结构中很容易的知道正则表达式支持的语法。
如何检查正则表达式的AST?
可以通过astexplorer.net网站直观查看:
通过将parse的语言切换到RegExp,可以可视化正则表达式的AST。
如前所述,AST 是按照语法来组织的一棵树,那么从它的结构上自然能容易地理清各种语法。
所以我们从AST的角度来学习各种语法:
/abc/
先说简单的,/abc/这样的正则性可以匹配 abc 的字符串,它的AST是这样的:
3 Char,值分别为A,B,C,类型简单。之后的匹配就是遍历AST,分别匹配这三个字符。
我们用exec的api进行了测试:
第0个元素是匹配字符串,index是匹配字符串的起始下标。Input是输入字符串。
让我们再次尝试特殊字符:
/\d\d\d/
/\d \ d/表示匹配三个数字,\ d是规则支持的具有特殊含义的元字符。
通过AST,我们还可以看到,虽然它们是Char,但类型是meta:
您可以通过元字符\d匹配任何数字:
哪些是元字符,哪些是简单字符,通过AST可以一目了然。
/[abc]/
正则性支持通过[]的方式指定一组字符,也就是说可以匹配任意一个字符。
通过AST,我们还可以看到它被包裹了一层CharacterClass,意思是字符类,也就是可以匹配它包含的任何字符。
测试确实是这样的:
/a{1,3}/
正则表达式支持以{from,to}的形式指定字符重复的次数,
例如,/b{1,3}/表示字符B重复1到3次,而/[abc]{1,3}/表示这个a/b/c字符类重复1到3次。
从AST中可以看出,这种语法称为重复:
他有一个量词属性来表示量词,其中类型是范围,从1到3。
正则性也支持一些量词的缩写,比如1到无数次,*代表0到无数次,0或1次。
它们是不同类型的量词:
可能有同学会问,这里的贪婪属性是什么意思?
Greedy表示贪婪,这个属性表示这个重复是贪婪的还是非贪婪的。
如果在量词后面加一个?你会发现greedy变成false,也就是切换到非greedy匹配:
贪与不贪是什么意思?
让我们来看一个例子。
默认重复的匹配是贪婪的,只要满足条件,总是会匹配的,所以acbac这里可以匹配。
量词后面加一?它被切换到非贪婪,并且它将只匹配第一个:
这就是贪婪匹配和非贪婪匹配。我们可以通过AST清楚的知道贪婪和非贪婪是针对重复语法来说的,默认是贪婪匹配,在量词后加个 ? 就可以切换到非贪婪。。
(aaa)bbb(ccc)
正则表达式支持将部分匹配的字符串放入子组,并通过()返回。
浏览一下AST:
相应的AST称为Group。
您会发现它有一个捕获属性,默认情况下是这样的:
这是什么意思?
这是子组捕获的语法。
如果不想捕获子群,可以写(?aaa)
看,捕捉已经变成假的了。
捕捉和不捕捉有什么区别?
让我们试试:
哦,原来Group的捕捉属性是指是否提取。
我们可以通过AST,捕获是针对子组来说的,默认是捕获,也就是提取子组的内容,可以通过 ?: 切换到非捕获,就不会提取子组的内容了。看到
我们已经熟悉了使用AST来理解常规语法,所以让我们看看最难的部分:
/bbb(?=ccc)/
正则表达式受(?=xxx)表达先行断言的语法,用于判断一个字符串前面是否有一个字符串。
通过AST,你可以看到这个语法叫做断言,类型是lookahead,也就是向前看,它只匹配前面的意思:
这是什么意思?为什么要写这个?和/bbb(ccc)/和/bbb(?ccc)/有什么区别?
让我们试试:
从结果可以看出:
/bbb(ccc)/匹配ccc的子组并提取该子组,因为捕获了默认子组。
/bbb(?ccc)/匹配了ccc的子群却因为我们通过了而没有提取?将子组设置为不采集。
/bbb(?=ccc)/匹配ccc的子组未被提取,表示它也未被捕获。它和?的区别在于:匹配结果中不出现ccc。
这就是前瞻断言的本质:先行断言代表某段字符串前面是某段字符串,对应的子组是非捕获的,而且断言的字符串不会出现在匹配结果中。。
如果后面没有那个字符串,就不匹配:
:
/bbb(?!ccc)/
?=改成了?之后,意思就变了。浏览一下AST:
尽管先行断言首先被断言,但是还有一个附加的属性,其负值为真。
这个意思很明显。原来是前面有某串,否定之后就没有前面有某串了。
那么匹配结果正好相反:
现在,如果前面不是某个字符串,就会匹配。这是否定先行断言。
/(?=aaa)bbb/
有一个前导断言,自然也有一个尾随断言,即跟随某个字符串进行匹配。
同样,也可以否认:
(?=aaa)对应的AST很容易想到,就是lookbehind断言:
(?Aaa)对应的AST是添加一个否定属性:
前断言和后断言是最难理解的正则表达式语法。AST是不是更好理解~
总结
正则表达式是一个非常方便的处理字符串的工具,但是它的学习还是有些难度的。许多人对贪婪匹配、非贪婪匹配、捕获子组、非捕获子组、第一个断言和最后一个断言等语法感到困惑。
我推荐通过AST学习规律性,AST是按照语法结构组织的对象树。通过AST节点的名称和属性可以很容易地整理出各种语法。
例如,我们通过AST明确表示:
重复语法(Repetition)就是字符 + 量词的形式,默认是贪婪匹配(greedy 为 true),代表一直匹配到不匹配为止,量词后加个 ? 就切换成了非贪婪匹配,匹配到一个字符就停止。
子组语法(Group)是用于提取某段字符串的,默认是捕获(capturing 为 true),代表需要提取,可以通过 (?:xxx)切换到非捕获,只匹配不提取。
断言语法(Assertion)代表前面或后面有某段字符串,分为先行断言(lookahead assertion)和后行断言(lookbehind assertion),语法分别是(?=xxx)和 (?=xxx),可以通过把 = 换成 ! 来表示否定(negative 为 true),意思正好反过来。
是各种文档对语法理解深刻还是编译人员对语法理解深刻?
不用问,肯定是编译器!
那么通过它根据语法解析的语法树来学习语法,自然比通过文档来学习语法要好。
正则表达式如此,其他语法的学习也是如此。如果你能用AST学习语法,你就不需要阅读文档。
更多关于node的信息,请访问:nodejs教程!就是这样快速掌握正则表达式的。通过AST学习常规语法!更多详情请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。