正则表达式之:零宽断言不『消费』

2024-09-03 13:08

本文主要是介绍正则表达式之:零宽断言不『消费』,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

想理解零宽断言只需要记住一句口诀:零宽断言只『占位』,不『消费』。


没错,零宽断言就是个去麦当劳写作业的小学生~

之所以想写这篇博客是因为昨天看了@玉伯也叫射雕 的最新博文: 《正则表达式中的向后匹配》。在这篇文章中,他提到了零宽断言的相关知识,但最核心的知识点却没涉及,即:零宽断言的用法类似普通的正则子表达式(也叫做分组),但不『消费』字符串。

何为『消费』呢?先来看一道题:

设:有字符串 var s = 'aaalllsss0tAAAnnn999';

问:请找出所有在 3个连续相同字符 前的相邻 3个连续相同字符

(题目是有点绕口…主要是因为 JS 支持的正则太弱了,所以为了适应它,只好这样出题。)

答案:

目测是:aaa, lll, AAA, nnn。用 JS 完成的话:

var re2 = /(\w)\1{2}(?=(\w)\2{2})/g;  // 用到了 *零宽度正预测先行断言*
console.log(s.match(re2));  // 输出 => [ 'aaa', 'lll', 'AAA', 'nnn' ]

零宽度正预测先行断言 这名字比较绕,忽略就好,理解了零宽断言之后,也没人去记住这些名字。

之所以要使用 零宽度正预测先行断言 来完成这题是因为字符串 s 中存在重叠的 3个连续相同字符,如果使用普通分组来做的话,就会不小心『消费』了那些字符而导致对他们的匹配被忽略。

不熟悉零宽断言的朋友,可能会使用如下的正则来完成这题:

var re1 = /((\w)\2{2})(\w)\3{2}/g;

这个正则输出的结果是:[‘aaa’, 'AAA’],是不合我们预期的。

我们来看看用 re1 进行匹配时,re1 是如何『消费』我们的字符串 s 的:

var s = 'aaalllsss0tAAAnnn999';
var re1 = /((\w)\2{2})(\w)\3{2}/g;console.log("s is: " + s);
console.log();var res;
while(res=re1.exec(s)) {console.log("match result: " + res[1] + ".","re1 comsumed: " + res[0],"re1.lastindex: " + re1.lastIndex,"remain string: " + s.slice(re1.lastIndex));
}

输出的结果是:

s is: aaalllsss0tAAAnnn999re1 result: aaa. re1 comsumed: aaalll, re1.lastindex: 6, remain string: sss0tAAAnnn999
re1 result: AAA. re1 comsumed: AAAnnn, re1.lastindex: 17, remain string: 999

什么是 lastIndex 呢?在 JS 中,我们可以使用 RegExp#exec 对某个字符串进行多次匹配,为了多次匹配的结果不重复,RegExp 每匹配完一次后,就记录好下一次匹配的起始位置,这个位置就是 RegExp#lastIndex。

从上面输出结果我们可以看到,re1 在匹配到了 'aaa’ 后,它就『消费』了 'aaalll’ 这个字符串,到了索引 6 这个位置。

我们再来看看用 re2 进行匹配时,re2 是如何『消费』字符串的:

re2 result: aaa. re2 comsumed: aaa, re2.lastindex: 3, remain string: lllsss0tAAAnnn999
re2 result: lll. re2 comsumed: lll, re2.lastindex: 6, remain string: sss0tAAAnnn999
re2 result: AAA. re2 comsumed: AAA, re2.lastindex: 14, remain string: nnn999
re2 result: nnn. re2 comsumed: nnn, re2.lastindex: 17, remain string: 999

代码就不贴了,我们直接来看输出。由于零宽断言不『消费』字符串,所以当我们第一次匹配到了 'aaa’ 后,re2 的 lastIndex 是在 3 这个位置,在其后的匹配中,它从 3 开始,又到 6 暂停。


引入了『消费』的概念后,我相信是可以帮助大家更好理解零宽断言这个概念的,只是不知上面的栗子举得好不好。

好了,let me 再介绍介绍零宽断言。零宽断言一共有四种,而且他们的名字一个比一个酷,不过关键是没有人能记得住(-_-||):

声明:以下内容引用自 《正则表达式30分钟入门教程》,我谨代表大陆人民向原作者致以深深的谢意!

(?=exp)也叫零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I’m singing while you’re dancing.时,它会匹配sing和danc。

(?<=exp)也叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp。比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。

零宽度负预测先行断言(?!exp),断言此位置的后面不能匹配表达式exp。例如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字;\b((?!abc)\w)+\b匹配不包含连续字符串abc的单词。

同理,我们可以用(?<!exp),零宽度负回顾后发断言来断言此位置的前面不能匹配表达式exp:(?<![a-z])\d{7}匹配前面不是小写字母的七位数字。


那么有同学要问:什么时候使用普通正则分组?什么时候使用零宽断言呢?

答:根据在下 Alsotang 本科三年编篡无数野代码的深厚经验来说,大部分时间使用普通正则分组;在上下文必要时,在你的队友也理解零宽断言时,才使用零宽断言。

好了,文章到此就结束了,再考大家一题。

问:有字符串

var web_development = "python php ruby javascript jsonp perhapsphpisoutdated";

,如何找出其中 包含 'p’ 但不包含 'ph’ 的所有单词?

答案:

# coffeescript
web_development.split(' ').filter (s) ->(s.contains('p') and not s.contains('ph'))
.join(' ')

这篇关于正则表达式之:零宽断言不『消费』的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1133040

相关文章

SpringBoot中如何使用Assert进行断言校验

《SpringBoot中如何使用Assert进行断言校验》Java提供了内置的assert机制,而Spring框架也提供了更强大的Assert工具类来帮助开发者进行参数校验和状态检查,下... 目录前言一、Java 原生assert简介1.1 使用方式1.2 示例代码1.3 优缺点分析二、Spring Fr

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

JavaSE正则表达式用法总结大全

《JavaSE正则表达式用法总结大全》正则表达式就是由一些特定的字符组成,代表的是一个规则,:本文主要介绍JavaSE正则表达式用法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录常用的正则表达式匹配符正则表China编程达式常用的类Pattern类Matcher类PatternSynta

Python中re模块结合正则表达式的实际应用案例

《Python中re模块结合正则表达式的实际应用案例》Python中的re模块是用于处理正则表达式的强大工具,正则表达式是一种用来匹配字符串的模式,它可以在文本中搜索和匹配特定的字符串模式,这篇文章主... 目录前言re模块常用函数一、查看文本中是否包含 A 或 B 字符串二、替换多个关键词为统一格式三、提

python常用的正则表达式及作用

《python常用的正则表达式及作用》正则表达式是处理字符串的强大工具,Python通过re模块提供正则表达式支持,本文给大家介绍python常用的正则表达式及作用详解,感兴趣的朋友跟随小编一起看看吧... 目录python常用正则表达式及作用基本匹配模式常用正则表达式示例常用量词边界匹配分组和捕获常用re

正则表达式r前缀使用指南及如何避免常见错误

《正则表达式r前缀使用指南及如何避免常见错误》正则表达式是处理字符串的强大工具,但它常常伴随着转义字符的复杂性,本文将简洁地讲解r的作用、基本原理,以及如何在实际代码中避免常见错误,感兴趣的朋友一... 目录1. 字符串的双重翻译困境2. 为什么需要 r?3. 常见错误和正确用法4. Unicode 转换的

Python正则表达式语法及re模块中的常用函数详解

《Python正则表达式语法及re模块中的常用函数详解》这篇文章主要给大家介绍了关于Python正则表达式语法及re模块中常用函数的相关资料,正则表达式是一种强大的字符串处理工具,可以用于匹配、切分、... 目录概念、作用和步骤语法re模块中的常用函数总结 概念、作用和步骤概念: 本身也是一个字符串,其中

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Python中使用正则表达式精准匹配IP地址的案例

《Python中使用正则表达式精准匹配IP地址的案例》Python的正则表达式(re模块)是完成这个任务的利器,但你知道怎么写才能准确匹配各种合法的IP地址吗,今天我们就来详细探讨这个问题,感兴趣的朋... 目录为什么需要IP正则表达式?IP地址的基本结构基础正则表达式写法精确匹配0-255的数字验证IP地

正则表达式高级应用与性能优化记录

《正则表达式高级应用与性能优化记录》本文介绍了正则表达式的高级应用和性能优化技巧,包括文本拆分、合并、XML/HTML解析、数据分析、以及性能优化方法,通过这些技巧,可以更高效地利用正则表达式进行复杂... 目录第6章:正则表达式的高级应用6.1 模式匹配与文本处理6.1.1 文本拆分6.1.2 文本合并6